/*
* 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 .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.ext.rbvi.chimera;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import jalview.api.FeatureRenderer;
import jalview.api.structures.JalviewStructureDisplayI;
import jalview.bin.Cache;
import jalview.bin.Jalview;
import jalview.datamodel.DBRefEntry;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.gui.AlignFrame;
import jalview.gui.Desktop;
import jalview.gui.JvOptionPane;
import jalview.gui.Preferences;
import jalview.gui.StructureViewer;
import jalview.gui.StructureViewer.ViewerType;
import jalview.io.DataSourceType;
import jalview.io.FileLoader;
import jalview.structure.StructureMapping;
import jalview.structure.StructureSelectionManager;
import jalview.ws.sifts.SiftsClient;
import jalview.ws.sifts.SiftsException;
import jalview.ws.sifts.SiftsSettings;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Vector;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@Test(singleThreaded = true)
public class JalviewChimeraView
{
@BeforeClass(alwaysRun = true)
public void setUpJvOptionPane()
{
JvOptionPane.setInteractiveMode(false);
JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
}
private JalviewStructureDisplayI chimeraViewer;
/**
* @throws java.lang.Exception
*/
@BeforeClass(alwaysRun = true)
public static void setUpBeforeClass() throws Exception
{
Jalview.main(new String[] { "-noquestionnaire", "-nonews", "-props",
"test/jalview/ext/rbvi/chimera/testProps.jvprops" });
Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
ViewerType.CHIMERA.name());
Cache.setProperty("SHOW_ANNOTATIONS", "false");
Cache.setProperty(Preferences.STRUCT_FROM_PDB, "false");
Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
ViewerType.CHIMERA.name());
Cache.setProperty("MAP_WITH_SIFTS", "true");
// TODO this should not be necessary!
SiftsSettings.setMapWithSifts(true);
}
/**
* @throws java.lang.Exception
*/
@AfterClass(alwaysRun = true)
public static void tearDownAfterClass() throws Exception
{
Desktop.instance.closeAll_actionPerformed(null);
}
@AfterMethod(alwaysRun = true)
public void tearDownAfterTest() throws Exception
{
SiftsClient.setMockSiftsFile(null);
if (chimeraViewer != null)
{
chimeraViewer.closeViewer(true);
}
}
/**
* Load 1GAQ and view the first structure for which a PDB id is found. Note no
* network connection is needed - PDB file is read locally, SIFTS fetch fails
* so mapping falls back to Needleman-Wunsch - ok for this test.
*/
// External as local install of Chimera required
@Test(groups = { "External" })
public void testSingleSeqViewChimera()
{
String inFile = "examples/1gaq.txt";
AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
DataSourceType.FILE);
assertNotNull(af, "Failed to create AlignFrame");
SequenceI sq = af.getViewport().getAlignment().getSequenceAt(0);
assertEquals(sq.getName(), "1GAQ|A");
SequenceI dsq = sq.getDatasetSequence();
Vector pdbIds = dsq.getAllPDBEntries();
assertEquals(pdbIds.size(), 1);
PDBEntry pdbEntry = pdbIds.get(0);
assertEquals(pdbEntry.getId(), "1GAQ");
StructureViewer structureViewer = new StructureViewer(af.getViewport()
.getStructureSelectionManager());
chimeraViewer = structureViewer.viewStructures(pdbEntry,
new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
.getBinding();
/*
* Wait for viewer load thread to complete
*/
while (!binding.isFinishedInit())
{
try
{
Thread.sleep(500);
} catch (InterruptedException e)
{
}
}
assertTrue(binding.isChimeraRunning(), "Failed to start Chimera");
assertEquals(chimeraViewer.getBinding().getPdbCount(), 1);
chimeraViewer.closeViewer(true);
chimeraViewer = null;
return;
}
/**
* Test for writing Jalview features as attributes on mapped residues in
* Chimera. Note this uses local copies of PDB and SIFTS file, no network
* connection required.
*
* @throws IOException
* @throws SiftsException
*/
// External as this requires a local install of Chimera
@Test(groups = { "External" })
public void testTransferFeatures() throws IOException, SiftsException
{
String inFile = "examples/uniref50.fa";
AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
DataSourceType.FILE);
assertNotNull(af, "Failed to create AlignFrame");
SequenceI sq = af.getViewport().getAlignment().findName("FER2_ARATH");
assertNotNull(sq, "Didn't find FER2_ARATH");
/*
* need a Uniprot dbref for SIFTS mapping to work!!
*/
sq.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
/*
* use local test PDB and SIFTS files
*/
String pdbFilePath = new File(
"test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
String siftsFilePath = new File(
"test/jalview/ext/rbvi/chimera/4zho.xml.gz")
.getPath();
SiftsClient.setMockSiftsFile(new File(siftsFilePath));
StructureViewer structureViewer = new StructureViewer(af.getViewport()
.getStructureSelectionManager());
chimeraViewer = structureViewer.viewStructures(pdbEntry,
new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
.getBinding();
do
{
try
{
Thread.sleep(500);
} catch (InterruptedException e)
{
}
} while (!binding.isFinishedInit());
assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
assertEquals(binding.getPdbCount(), 1);
/*
* check mapping is (sequence) 53-145 to (structure) 2-94 A/B
* (or possibly 52-145 to 1-94 - see JAL-2319)
*/
StructureSelectionManager ssm = binding.getSsm();
String pdbFile = binding.getStructureFiles()[0];
StructureMapping[] mappings = ssm.getMapping(pdbFile);
assertTrue(mappings[0].getMappingDetailsOutput().contains("SIFTS"),
"Failed to perform SIFTS mapping");
assertEquals(mappings.length, 2);
assertEquals(mappings[0].getChain(), "A");
assertEquals(mappings[0].getPDBResNum(53), 2);
assertEquals(mappings[0].getPDBResNum(145), 94);
assertEquals(mappings[1].getChain(), "B");
assertEquals(mappings[1].getPDBResNum(53), 2);
assertEquals(mappings[1].getPDBResNum(145), 94);
/*
* now add some features to FER2_ARATH
*/
// feature on a sequence region not mapped to structure:
sq.addSequenceFeature(new SequenceFeature("transit peptide",
"chloroplast", 1, 51, Float.NaN, null));
// feature on a region mapped to structure:
sq.addSequenceFeature(new SequenceFeature("domain",
"2Fe-2S ferredoxin-type", 55, 145, Float.NaN, null));
// on sparse positions of the sequence
sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
"Iron-Sulfur (2Fe-2S)", 91, 91, Float.NaN, null));
sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
"Iron-Sulfur (2Fe-2S)", 96, 96, Float.NaN, null));
// on a sequence region that is partially mapped to structure:
sq.addSequenceFeature(new SequenceFeature("helix", null, 50, 60,
Float.NaN, null));
// and again:
sq.addSequenceFeature(new SequenceFeature("chain", null, 50, 70,
Float.NaN, null));
// add numeric valued features - score is set as attribute value
sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 62,
62, -2.1f, null));
sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 65,
65, 3.6f, null));
sq.addSequenceFeature(new SequenceFeature("RESNUM", "ALA: 2 4zhoA",
53, 53, Float.NaN, null));
/*
* set all features visible except for chain
*/
af.setShowSeqFeatures(true);
FeatureRenderer fr = af.getFeatureRenderer();
fr.setVisible("transit peptide");
fr.setVisible("domain");
fr.setVisible("metal ion-binding site");
fr.setVisible("helix");
fr.setVisible("kd");
fr.setVisible("RESNUM");
/*
* 'perform' menu action to copy visible features to
* attributes in Chimera
*/
// TODO rename and pull up method to binding interface
// once functionality is added for Jmol as well
binding.sendFeaturesToViewer(af.getViewport().getAlignPanel());
/*
* give Chimera time to open the commands file and execute it
*/
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
}
/*
* ask Chimera for its residue attribute names
*/
List reply = binding.sendChimeraCommand("list resattr", true);
// prefixed and sanitised attribute names for Jalview features:
assertTrue(reply.contains("resattr jv_domain"));
assertTrue(reply.contains("resattr jv_metal_ion_binding_site"));
assertTrue(reply.contains("resattr jv_helix"));
assertTrue(reply.contains("resattr jv_kd"));
assertTrue(reply.contains("resattr jv_RESNUM"));
// feature is not on a mapped region - no attribute created
assertFalse(reply.contains("resattr jv_transit_peptide"));
// feature is not visible - no attribute created
assertFalse(reply.contains("resattr jv_chain"));
/*
* ask Chimera for residues with an attribute
* 91 and 96 on sequence --> residues 40 and 45 on chains A and B
*/
reply = binding.sendChimeraCommand(
"list resi att jv_metal_ion_binding_site", true);
assertEquals(reply.size(), 4);
assertTrue(reply
.contains("residue id #0:40.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
assertTrue(reply
.contains("residue id #0:45.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
assertTrue(reply
.contains("residue id #0:40.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
assertTrue(reply
.contains("residue id #0:45.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
/*
* check attributes with score values
* sequence positions 62 and 65 --> residues 11 and 14 on chains A and B
*/
reply = binding.sendChimeraCommand("list resi att jv_kd", true);
assertEquals(reply.size(), 4);
assertTrue(reply.contains("residue id #0:11.A jv_kd -2.1 index 11"));
assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
assertTrue(reply.contains("residue id #0:11.B jv_kd -2.1 index 11"));
assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
/*
* list residues with positive kd score
*/
reply = binding.sendChimeraCommand(
"list resi spec :*/jv_kd>0 attr jv_kd", true);
assertEquals(reply.size(), 2);
assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
SiftsClient.setMockSiftsFile(null);
chimeraViewer.closeViewer(true);
chimeraViewer = null;
}
/**
* Test for creating Jalview features from attributes on mapped residues in
* Chimera. Note this uses local copies of PDB and SIFTS file, no network
* connection required.
*
* @throws IOException
* @throws SiftsException
*/
// External as this requires a local install of Chimera
@Test(groups = { "External" })
public void testGetAttributes() throws IOException, SiftsException
{
String inFile = "examples/uniref50.fa";
AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
DataSourceType.FILE);
assertNotNull(af, "Failed to create AlignFrame");
SequenceI fer2Arath = af.getViewport().getAlignment()
.findName("FER2_ARATH");
assertNotNull(fer2Arath, "Didn't find FER2_ARATH");
/*
* need a Uniprot dbref for SIFTS mapping to work!!
*/
fer2Arath.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
/*
* use local test PDB and SIFTS files
*/
String pdbFilePath = new File(
"test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
String siftsFilePath = new File(
"test/jalview/ext/rbvi/chimera/4zho.xml.gz")
.getPath();
SiftsClient.setMockSiftsFile(new File(siftsFilePath));
StructureViewer structureViewer = new StructureViewer(af.getViewport()
.getStructureSelectionManager());
chimeraViewer = structureViewer.viewStructures(pdbEntry,
new SequenceI[] { fer2Arath }, af.getCurrentView()
.getAlignPanel());
JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
.getBinding();
do
{
try
{
Thread.sleep(500);
} catch (InterruptedException e)
{
}
} while (!binding.isFinishedInit());
assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
assertEquals(binding.getPdbCount(), 1);
/*
* 'perform' menu action to copy visible features to
* attributes in Chimera
*/
// TODO rename and pull up method to binding interface
// once functionality is added for Jmol as well
binding.copyStructureAttributesToFeatures("isHelix", af.getViewport()
.getAlignPanel());
/*
* verify 22 residues have isHelix feature
* (may merge into ranges in future)
*/
af.setShowSeqFeatures(true);
FeatureRenderer fr = af.getFeatureRenderer();
fr.setVisible("isHelix");
for (int res = 75; res <= 83; res++)
{
checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
}
for (int res = 117; res <= 123; res++)
{
checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
}
for (int res = 129; res <= 131; res++)
{
checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
}
for (int res = 143; res <= 145; res++)
{
checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
}
/*
* fetch a numeric valued attribute
*/
binding.copyStructureAttributesToFeatures("phi", af.getViewport()
.getAlignPanel());
fr.setVisible("phi");
List fs = fer2Arath.getFeatures().findFeatures(54, 54);
assertEquals(fs.size(), 3);
/*
* order of returned features is not guaranteed
*/
assertTrue("RESNUM".equals(fs.get(0).getType())
|| "RESNUM".equals(fs.get(1).getType())
|| "RESNUM".equals(fs.get(2).getType()));
assertTrue(fs.contains(new SequenceFeature("phi", "A", 54, 54,
-131.0713f, "Chimera")));
assertTrue(fs.contains(new SequenceFeature("phi", "B", 54, 54,
-127.39512f, "Chimera")));
/*
* tear down - also in AfterMethod
*/
SiftsClient.setMockSiftsFile(null);
chimeraViewer.closeViewer(true);
chimeraViewer = null;
}
/**
* Helper method to verify new feature at a sequence position
*
* @param seq
* @param fr
* @param res
* @param featureType
*/
protected void checkFeaturesAtRes(SequenceI seq, FeatureRenderer fr,
int res, String featureType)
{
String where = "at position " + res;
List fs = seq.getFeatures().findFeatures(res, res);
assertEquals(fs.size(), 2, where);
assertEquals(fs.get(0).getType(), "RESNUM", where);
SequenceFeature sf = fs.get(1);
assertEquals(sf.getType(), featureType, where);
assertEquals(sf.getFeatureGroup(), "Chimera", where);
assertEquals(sf.getDescription(), "True", where);
assertEquals(sf.getScore(), Float.NaN, where);
}
}