2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ext.rbvi.chimera;
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotNull;
26 import static org.testng.Assert.assertTrue;
28 import jalview.api.FeatureRenderer;
29 import jalview.api.structures.JalviewStructureDisplayI;
30 import jalview.bin.Cache;
31 import jalview.bin.Jalview;
32 import jalview.datamodel.DBRefEntry;
33 import jalview.datamodel.PDBEntry;
34 import jalview.datamodel.SequenceFeature;
35 import jalview.datamodel.SequenceI;
36 import jalview.gui.AlignFrame;
37 import jalview.gui.Desktop;
38 import jalview.gui.JvOptionPane;
39 import jalview.gui.Preferences;
40 import jalview.gui.StructureViewer;
41 import jalview.gui.StructureViewer.ViewerType;
42 import jalview.io.DataSourceType;
43 import jalview.io.FileLoader;
44 import jalview.structure.StructureMapping;
45 import jalview.structure.StructureSelectionManager;
46 import jalview.ws.sifts.SiftsClient;
47 import jalview.ws.sifts.SiftsException;
48 import jalview.ws.sifts.SiftsSettings;
51 import java.io.IOException;
52 import java.util.List;
53 import java.util.Vector;
55 import org.testng.annotations.AfterClass;
56 import org.testng.annotations.AfterMethod;
57 import org.testng.annotations.BeforeClass;
58 import org.testng.annotations.Test;
60 @Test(singleThreaded = true)
61 public class JalviewChimeraView
64 @BeforeClass(alwaysRun = true)
65 public void setUpJvOptionPane()
67 JvOptionPane.setInteractiveMode(false);
68 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
71 private JalviewStructureDisplayI chimeraViewer;
74 * @throws java.lang.Exception
76 @BeforeClass(alwaysRun = true)
77 public static void setUpBeforeClass() throws Exception
79 Jalview.main(new String[] { "-noquestionnaire", "-nonews", "-props",
80 "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
81 Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
82 ViewerType.CHIMERA.name());
83 Cache.setProperty("SHOW_ANNOTATIONS", "false");
84 Cache.setProperty(Preferences.STRUCT_FROM_PDB, "false");
85 Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
86 ViewerType.CHIMERA.name());
87 Cache.setProperty("MAP_WITH_SIFTS", "true");
88 // TODO this should not be necessary!
89 SiftsSettings.setMapWithSifts(true);
93 * @throws java.lang.Exception
95 @AfterClass(alwaysRun = true)
96 public static void tearDownAfterClass() throws Exception
98 Desktop.instance.closeAll_actionPerformed(null);
101 @AfterMethod(alwaysRun = true)
102 public void tearDownAfterTest() throws Exception
104 SiftsClient.setMockSiftsFile(null);
105 if (chimeraViewer != null)
107 chimeraViewer.closeViewer(true);
112 * Load 1GAQ and view the first structure for which a PDB id is found. Note no
113 * network connection is needed - PDB file is read locally, SIFTS fetch fails
114 * so mapping falls back to Needleman-Wunsch - ok for this test.
116 // External as local install of Chimera required
117 @Test(groups = { "External" })
118 public void testSingleSeqViewChimera()
120 String inFile = "examples/1gaq.txt";
121 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
122 DataSourceType.FILE);
123 assertNotNull(af, "Failed to create AlignFrame");
124 SequenceI sq = af.getViewport().getAlignment().getSequenceAt(0);
125 assertEquals(sq.getName(), "1GAQ|A");
126 SequenceI dsq = sq.getDatasetSequence();
127 Vector<PDBEntry> pdbIds = dsq.getAllPDBEntries();
128 assertEquals(pdbIds.size(), 1);
129 PDBEntry pdbEntry = pdbIds.get(0);
130 assertEquals(pdbEntry.getId(), "1GAQ");
131 StructureViewer structureViewer = new StructureViewer(af.getViewport()
132 .getStructureSelectionManager());
133 chimeraViewer = structureViewer.viewStructures(pdbEntry,
134 new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
135 JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
139 * Wait for viewer load thread to complete
141 while (!binding.isFinishedInit())
146 } catch (InterruptedException e)
151 assertTrue(binding.isChimeraRunning(), "Failed to start Chimera");
153 assertEquals(chimeraViewer.getBinding().getPdbCount(), 1);
154 chimeraViewer.closeViewer(true);
155 chimeraViewer = null;
160 * Test for writing Jalview features as attributes on mapped residues in
161 * Chimera. Note this uses local copies of PDB and SIFTS file, no network
162 * connection required.
164 * @throws IOException
165 * @throws SiftsException
167 // External as this requires a local install of Chimera
168 @Test(groups = { "External" })
169 public void testTransferFeatures() throws IOException, SiftsException
171 String inFile = "examples/uniref50.fa";
172 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
173 DataSourceType.FILE);
174 assertNotNull(af, "Failed to create AlignFrame");
175 SequenceI sq = af.getViewport().getAlignment().findName("FER2_ARATH");
176 assertNotNull(sq, "Didn't find FER2_ARATH");
179 * need a Uniprot dbref for SIFTS mapping to work!!
181 sq.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
184 * use local test PDB and SIFTS files
186 String pdbFilePath = new File(
187 "test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
188 PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
189 String siftsFilePath = new File(
190 "test/jalview/ext/rbvi/chimera/4zho.xml.gz")
192 SiftsClient.setMockSiftsFile(new File(siftsFilePath));
194 StructureViewer structureViewer = new StructureViewer(af.getViewport()
195 .getStructureSelectionManager());
196 chimeraViewer = structureViewer.viewStructures(pdbEntry,
197 new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
199 JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
206 } catch (InterruptedException e)
209 } while (!binding.isFinishedInit());
211 assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
213 assertEquals(binding.getPdbCount(), 1);
216 * check mapping is (sequence) 53-145 to (structure) 2-94 A/B
217 * (or possibly 52-145 to 1-94 - see JAL-2319)
219 StructureSelectionManager ssm = binding.getSsm();
220 String pdbFile = binding.getStructureFiles()[0];
221 StructureMapping[] mappings = ssm.getMapping(pdbFile);
222 assertTrue(mappings[0].getMappingDetailsOutput().contains("SIFTS"),
223 "Failed to perform SIFTS mapping");
224 assertEquals(mappings.length, 2);
225 assertEquals(mappings[0].getChain(), "A");
226 assertEquals(mappings[0].getPDBResNum(53), 2);
227 assertEquals(mappings[0].getPDBResNum(145), 94);
228 assertEquals(mappings[1].getChain(), "B");
229 assertEquals(mappings[1].getPDBResNum(53), 2);
230 assertEquals(mappings[1].getPDBResNum(145), 94);
233 * now add some features to FER2_ARATH
235 // feature on a sequence region not mapped to structure:
236 sq.addSequenceFeature(new SequenceFeature("transit peptide",
237 "chloroplast", 1, 51, Float.NaN, null));
238 // feature on a region mapped to structure:
239 sq.addSequenceFeature(new SequenceFeature("domain",
240 "2Fe-2S ferredoxin-type", 55, 145, Float.NaN, null));
241 // on sparse positions of the sequence
242 sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
243 "Iron-Sulfur (2Fe-2S)", 91, 91, Float.NaN, null));
244 sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
245 "Iron-Sulfur (2Fe-2S)", 96, 96, Float.NaN, null));
246 // on a sequence region that is partially mapped to structure:
247 sq.addSequenceFeature(new SequenceFeature("helix", null, 50, 60,
250 sq.addSequenceFeature(new SequenceFeature("chain", null, 50, 70,
252 // add numeric valued features - score is set as attribute value
253 sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 62,
255 sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 65,
257 sq.addSequenceFeature(new SequenceFeature("RESNUM", "ALA: 2 4zhoA",
258 53, 53, Float.NaN, null));
261 * set all features visible except for chain
263 af.setShowSeqFeatures(true);
264 FeatureRenderer fr = af.getFeatureRenderer();
265 fr.setVisible("transit peptide");
266 fr.setVisible("domain");
267 fr.setVisible("metal ion-binding site");
268 fr.setVisible("helix");
270 fr.setVisible("RESNUM");
273 * 'perform' menu action to copy visible features to
274 * attributes in Chimera
276 // TODO rename and pull up method to binding interface
277 // once functionality is added for Jmol as well
278 binding.sendFeaturesToViewer(af.getViewport().getAlignPanel());
281 * give Chimera time to open the commands file and execute it
286 } catch (InterruptedException e)
291 * ask Chimera for its residue attribute names
293 List<String> reply = binding.sendChimeraCommand("list resattr", true);
294 // prefixed and sanitised attribute names for Jalview features:
295 assertTrue(reply.contains("resattr jv_domain"));
296 assertTrue(reply.contains("resattr jv_metal_ion_binding_site"));
297 assertTrue(reply.contains("resattr jv_helix"));
298 assertTrue(reply.contains("resattr jv_kd"));
299 assertTrue(reply.contains("resattr jv_RESNUM"));
300 // feature is not on a mapped region - no attribute created
301 assertFalse(reply.contains("resattr jv_transit_peptide"));
302 // feature is not visible - no attribute created
303 assertFalse(reply.contains("resattr jv_chain"));
306 * ask Chimera for residues with an attribute
307 * 91 and 96 on sequence --> residues 40 and 45 on chains A and B
309 reply = binding.sendChimeraCommand(
310 "list resi att jv_metal_ion_binding_site", true);
311 assertEquals(reply.size(), 4);
313 .contains("residue id #0:40.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
315 .contains("residue id #0:45.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
317 .contains("residue id #0:40.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
319 .contains("residue id #0:45.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
322 * check attributes with score values
323 * sequence positions 62 and 65 --> residues 11 and 14 on chains A and B
325 reply = binding.sendChimeraCommand("list resi att jv_kd", true);
326 assertEquals(reply.size(), 4);
327 assertTrue(reply.contains("residue id #0:11.A jv_kd -2.1 index 11"));
328 assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
329 assertTrue(reply.contains("residue id #0:11.B jv_kd -2.1 index 11"));
330 assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
333 * list residues with positive kd score
335 reply = binding.sendChimeraCommand(
336 "list resi spec :*/jv_kd>0 attr jv_kd", true);
337 assertEquals(reply.size(), 2);
338 assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
339 assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
341 SiftsClient.setMockSiftsFile(null);
342 chimeraViewer.closeViewer(true);
343 chimeraViewer = null;
347 * Test for creating Jalview features from attributes on mapped residues in
348 * Chimera. Note this uses local copies of PDB and SIFTS file, no network
349 * connection required.
351 * @throws IOException
352 * @throws SiftsException
354 // External as this requires a local install of Chimera
355 @Test(groups = { "External" })
356 public void testGetAttributes() throws IOException, SiftsException
358 String inFile = "examples/uniref50.fa";
359 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
360 DataSourceType.FILE);
361 assertNotNull(af, "Failed to create AlignFrame");
362 SequenceI fer2Arath = af.getViewport().getAlignment()
363 .findName("FER2_ARATH");
364 assertNotNull(fer2Arath, "Didn't find FER2_ARATH");
367 * need a Uniprot dbref for SIFTS mapping to work!!
369 fer2Arath.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
372 * use local test PDB and SIFTS files
374 String pdbFilePath = new File(
375 "test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
376 PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
377 String siftsFilePath = new File(
378 "test/jalview/ext/rbvi/chimera/4zho.xml.gz")
380 SiftsClient.setMockSiftsFile(new File(siftsFilePath));
382 StructureViewer structureViewer = new StructureViewer(af.getViewport()
383 .getStructureSelectionManager());
384 chimeraViewer = structureViewer.viewStructures(pdbEntry,
385 new SequenceI[] { fer2Arath }, af.getCurrentView()
388 JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
395 } catch (InterruptedException e)
398 } while (!binding.isFinishedInit());
400 assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
402 assertEquals(binding.getPdbCount(), 1);
405 * 'perform' menu action to copy visible features to
406 * attributes in Chimera
408 // TODO rename and pull up method to binding interface
409 // once functionality is added for Jmol as well
410 binding.copyStructureAttributesToFeatures("isHelix", af.getViewport()
414 * verify 22 residues have isHelix feature
415 * (may merge into ranges in future)
417 af.setShowSeqFeatures(true);
418 FeatureRenderer fr = af.getFeatureRenderer();
419 fr.setVisible("isHelix");
420 for (int res = 75; res <= 83; res++)
422 checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
424 for (int res = 117; res <= 123; res++)
426 checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
428 for (int res = 129; res <= 131; res++)
430 checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
432 for (int res = 143; res <= 145; res++)
434 checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
438 * fetch a numeric valued attribute
440 binding.copyStructureAttributesToFeatures("phi", af.getViewport()
442 fr.setVisible("phi");
443 List<SequenceFeature> fs = fer2Arath.getFeatures().findFeatures(54, 54);
444 assertEquals(fs.size(), 3);
446 * order of returned features is not guaranteed
448 assertTrue("RESNUM".equals(fs.get(0).getType())
449 || "RESNUM".equals(fs.get(1).getType())
450 || "RESNUM".equals(fs.get(2).getType()));
451 assertTrue(fs.contains(new SequenceFeature("phi", "A", 54, 54,
452 -131.0713f, "Chimera")));
453 assertTrue(fs.contains(new SequenceFeature("phi", "B", 54, 54,
454 -127.39512f, "Chimera")));
457 * tear down - also in AfterMethod
459 SiftsClient.setMockSiftsFile(null);
460 chimeraViewer.closeViewer(true);
461 chimeraViewer = null;
465 * Helper method to verify new feature at a sequence position
472 protected void checkFeaturesAtRes(SequenceI seq, FeatureRenderer fr,
473 int res, String featureType)
475 String where = "at position " + res;
476 List<SequenceFeature> fs = seq.getFeatures().findFeatures(res, res);
478 assertEquals(fs.size(), 2, where);
479 assertEquals(fs.get(0).getType(), "RESNUM", where);
480 SequenceFeature sf = fs.get(1);
481 assertEquals(sf.getType(), featureType, where);
482 assertEquals(sf.getFeatureGroup(), "Chimera", where);
483 assertEquals(sf.getDescription(), "True", where);
484 assertEquals(sf.getScore(), Float.NaN, where);