action.calculate = Calculate
action.select_all = Select all
action.select_highlighted_columns = Select Highlighted Columns
- tooltip.select_highlighted_columns = Press B to mark highlighted columns, Ctrl-B to toggle, and Alt-B to mark all but highlighted columns
+ tooltip.select_highlighted_columns = Press B to mark highlighted columns, Ctrl-(or Cmd)-B to toggle, and Alt-B to mark all but highlighted columns
action.deselect_all = Deselect all
action.invert_selection = Invert selection
action.using_jmol = Using Jmol
label.align_structures = Align Structures
label.jmol = Jmol
label.chimera = Chimera
+label.create_chimera_attributes = Write Jalview features
+label.create_chimera_attributes_tip = Set Chimera residue attributes for visible features
label.sort_alignment_by_tree = Sort Alignment By Tree
label.mark_unlinked_leaves = Mark Unlinked Leaves
label.associate_leaves_with = Associate Leaves With
+ /* vim: set ts=2: */
+ /**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions, and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Redistributions must acknowledge that this software was
+ * originally developed by the UCSF Computer Graphics Laboratory
+ * under support by the NIH National Center for Research Resources,
+ * grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
package ext.edu.ucsf.rbvi.strucviz2;
import jalview.ws.HttpClientUtils;
"list selection level residue", true);
if (chimeraReply != null)
{
+ /*
+ * expect 0, 1 or more lines of the format
+ * residue id #0:43.A type GLY
+ * where we are only interested in the atomspec #0.43.A
+ */
for (String inputLine : chimeraReply)
{
String[] inputLineParts = inputLine.split("\\s+");
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.httpserver.AbstractRequestHandler;
+ import jalview.io.DataSourceType;
import jalview.schemes.ColourSchemeI;
import jalview.schemes.ResidueProperties;
import jalview.structure.AtomSpec;
import jalview.util.MessageManager;
import java.awt.Color;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
import java.net.BindException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
public abstract class JalviewChimeraBinding extends AAStructureBindingModel
{
+ public static final String CHIMERA_FEATURE_GROUP = "Chimera";
+
// Chimera clause to exclude alternate locations in atom selection
private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
*/
private Map<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<String, List<ChimeraModel>>();
- /*
- * the default or current model displayed if the model cannot be identified
- * from the selection message
- */
- private int frameNo = 0;
-
- private String lastCommand;
-
String lastHighlightCommand;
/*
* @param protocol
*/
public JalviewChimeraBinding(StructureSelectionManager ssm,
- PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
+ PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol)
{
super(ssm, pdbentry, sequenceIs, protocol);
viewer = new ChimeraManager(
chimeraListener.shutdown();
chimeraListener = null;
}
- lastCommand = null;
viewer = null;
releaseUIResources();
}
/**
- * Send a command to Chimera, and optionally log any responses.
+ * Send a command to Chimera, and optionally log and return any responses.
+ * <p>
+ * Does nothing, and returns null, if the command is the same as the last one
+ * sent [why?].
*
* @param command
- * @param logResponse
+ * @param getResponse
*/
- public void sendChimeraCommand(final String command, boolean logResponse)
+ public List<String> sendChimeraCommand(final String command,
+ boolean getResponse)
{
if (viewer == null)
{
// ? thread running after viewer shut down
- return;
+ return null;
}
+ List<String> reply = null;
viewerCommandHistory(false);
- if (lastCommand == null || !lastCommand.equals(command))
+ if (true /*lastCommand == null || !lastCommand.equals(command)*/)
{
// trim command or it may never find a match in the replyLog!!
List<String> lastReply = viewer.sendChimeraCommand(command.trim(),
- logResponse);
- if (logResponse && debug)
+ getResponse);
+ if (getResponse)
{
- log("Response from command ('" + command + "') was:\n" + lastReply);
+ reply = lastReply;
+ if (debug)
+ {
+ log("Response from command ('" + command + "') was:\n"
+ + lastReply);
+ }
}
}
viewerCommandHistory(true);
- lastCommand = command;
+
+ return reply;
}
/**
}
AlignmentI alignment = alignmentv.getAlignment();
- for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands(
- files, sr, fr, alignment))
+ StructureMappingcommandSet colourBySequenceCommands = ChimeraCommands
+ .getColourBySequenceCommand(getSsm(), files, getSequence(), sr,
+ fr, alignment);
+ for (String command : colourBySequenceCommands.commands)
{
- for (String command : cpdbbyseq.commands)
- {
- sendAsynchronousCommand(command, COLOURING_CHIMERA);
- }
+ sendAsynchronousCommand(command, COLOURING_CHIMERA);
}
}
/**
- * @param files
- * @param sr
- * @param fr
- * @param alignment
- * @return
- */
- protected StructureMappingcommandSet[] getColourBySequenceCommands(
- String[] files, SequenceRenderer sr, FeatureRenderer fr,
- AlignmentI alignment)
- {
- return ChimeraCommands.getColourBySequenceCommand(getSsm(), files,
- getSequence(), sr, fr, alignment);
- }
-
- /**
* @param command
*/
protected void executeWhenReady(String command)
*/
public abstract void refreshPdbEntries();
- private int getModelNum(String modelFileName)
- {
- String[] mfn = getPdbFile();
- if (mfn == null)
- {
- return -1;
- }
- for (int i = 0; i < mfn.length; i++)
- {
- if (mfn[i].equalsIgnoreCase(modelFileName))
- {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * map between index of model filename returned from getPdbFile and the first
- * index of models from this file in the viewer. Note - this is not trimmed -
- * use getPdbFile to get number of unique models.
- */
- private int _modelFileNameMap[];
-
// ////////////////////////////////
// /StructureListener
@Override
* Parse model number, residue and chain for each selected position,
* formatted as #0:123.A or #1.2:87.B (#model.submodel:residue.chain)
*/
+ List<AtomSpec> atomSpecs = convertStructureResiduesToAlignment(selection);
+
+ /*
+ * Broadcast the selection (which may be empty, if the user just cleared all
+ * selections)
+ */
+ getSsm().mouseOverStructure(atomSpecs);
+ }
+
+ /**
+ * Converts a list of Chimera atomspecs to a list of AtomSpec representing the
+ * corresponding residues (if any) in Jalview
+ *
+ * @param structureSelection
+ * @return
+ */
+ protected List<AtomSpec> convertStructureResiduesToAlignment(
+ List<String> structureSelection)
+ {
List<AtomSpec> atomSpecs = new ArrayList<AtomSpec>();
- for (String atomSpec : selection)
+ for (String atomSpec : structureSelection)
{
- int colonPos = atomSpec.indexOf(":");
- if (colonPos == -1)
- {
- continue; // malformed
- }
-
- int hashPos = atomSpec.indexOf("#");
- String modelSubmodel = atomSpec.substring(hashPos + 1, colonPos);
- int dotPos = modelSubmodel.indexOf(".");
- int modelId = 0;
try
{
- modelId = Integer.valueOf(dotPos == -1 ? modelSubmodel
- : modelSubmodel.substring(0, dotPos));
- } catch (NumberFormatException e)
+ AtomSpec spec = AtomSpec.fromChimeraAtomspec(atomSpec);
+ String pdbfilename = getPdbFileForModel(spec.getModelNumber());
+ spec.setPdbFile(pdbfilename);
+ atomSpecs.add(spec);
+ } catch (IllegalArgumentException e)
{
- // ignore, default to model 0
+ System.err.println("Failed to parse atomspec: " + atomSpec);
}
+ }
+ return atomSpecs;
+ }
- String residueChain = atomSpec.substring(colonPos + 1);
- dotPos = residueChain.indexOf(".");
- int pdbResNum = Integer.parseInt(dotPos == -1 ? residueChain
- : residueChain.substring(0, dotPos));
-
- String chainId = dotPos == -1 ? "" : residueChain
- .substring(dotPos + 1);
-
- /*
- * Work out the pdbfilename from the model number
- */
- String pdbfilename = modelFileNames[frameNo];
- findfileloop: for (String pdbfile : this.chimeraMaps.keySet())
+ /**
+ * @param modelId
+ * @return
+ */
+ protected String getPdbFileForModel(int modelId)
+ {
+ /*
+ * Work out the pdbfilename from the model number
+ */
+ String pdbfilename = modelFileNames[0];
+ findfileloop: for (String pdbfile : this.chimeraMaps.keySet())
+ {
+ for (ChimeraModel cm : chimeraMaps.get(pdbfile))
{
- for (ChimeraModel cm : chimeraMaps.get(pdbfile))
+ if (cm.getModelNumber() == modelId)
{
- if (cm.getModelNumber() == modelId)
- {
- pdbfilename = pdbfile;
- break findfileloop;
- }
+ pdbfilename = pdbfile;
+ break findfileloop;
}
}
- atomSpecs.add(new AtomSpec(pdbfilename, chainId, pdbResNum, 0));
}
-
- /*
- * Broadcast the selection (which may be empty, if the user just cleared all
- * selections)
- */
- getSsm().mouseOverStructure(atomSpecs);
+ return pdbfilename;
}
private void log(String message)
*
* @return
*/
+ @Override
+ public List<String> getChainNames()
+ {
+ return chainNames;
+ }
/**
* Send a 'focus' command to Chimera to recentre the visible display
}
}
+ /**
+ * Constructs and send commands to Chimera to set attributes on residues for
+ * features visible in Jalview
+ *
+ * @param avp
+ */
+ public void sendFeaturesToViewer(AlignmentViewPanel avp)
+ {
+ // TODO refactor as required to pull up to an interface
+ AlignmentI alignment = avp.getAlignment();
+ FeatureRenderer fr = getFeatureRenderer(avp);
- @Override
- public List<String> getChainNames()
+ /*
+ * fr is null if feature display is turned off
+ */
+ if (fr == null)
+ {
+ return;
+ }
+
+ String[] files = getPdbFile();
+ if (files == null)
+ {
+ return;
+ }
+
+ StructureMappingcommandSet commandSet = ChimeraCommands
+ .getSetAttributeCommandsForFeatures(getSsm(), files,
+ getSequence(), fr, alignment);
+ String[] commands = commandSet.commands;
+ if (commands.length > 10)
+ {
+ sendCommandsByFile(commands);
+ }
+ else
+ {
+ for (String command : commands)
+ {
+ sendAsynchronousCommand(command, null);
+ }
+ }
+ }
+
+ /**
+ * Write commands to a temporary file, and send a command to Chimera to open
+ * the file as a commands script. For use when sending a large number of
+ * separate commands would overload the REST interface mechanism.
+ *
+ * @param commands
+ */
+ protected void sendCommandsByFile(String[] commands)
{
- return chainNames;
+ try
+ {
+ File tmp = File.createTempFile("chim", ".com");
+ tmp.deleteOnExit();
+ PrintWriter out = new PrintWriter(new FileOutputStream(tmp));
+ for (String command : commands)
+ {
+ out.println(command);
+ }
+ out.flush();
+ out.close();
+ String path = tmp.getAbsolutePath();
+ sendAsynchronousCommand("open cmd:" + path, null);
+ } catch (IOException e)
+ {
+ System.err
+ .println("Sending commands to Chimera via file failed with "
+ + e.getMessage());
+ }
}
+ /**
+ * Get Chimera residues which have the named attribute, find the mapped
+ * positions in the Jalview sequence(s), and set as sequence features
+ *
+ * @param attName
+ * @param alignmentPanel
+ */
+ public void copyStructureAttributesToFeatures(String attName,
+ AlignmentViewPanel alignmentPanel)
+ {
+ // todo pull up to AAStructureBindingModel (and interface?)
+
+ /*
+ * ask Chimera to list residues with the attribute, reporting its value
+ */
+ // this alternative command
+ // list residues spec ':*/attName' attr attName
+ // doesn't report 'None' values (which is good), but
+ // fails for 'average.bfactor' (which is bad):
+
+ String cmd = "list residues attr '" + attName + "'";
+ List<String> residues = sendChimeraCommand(cmd, true);
+
+ boolean featureAdded = createFeaturesForAttributes(attName, residues);
+ if (featureAdded)
+ {
+ alignmentPanel.getFeatureRenderer().featuresAdded();
+ }
+ }
+
+ /**
+ * Create features in Jalview for the given attribute name and structure
+ * residues.
+ *
+ * <pre>
+ * The residue list should be 0, 1 or more reply lines of the format:
+ * residue id #0:5.A isHelix -155.000836316 index 5
+ * or
+ * residue id #0:6.A isHelix None
+ * </pre>
+ *
+ * @param attName
+ * @param residues
+ * @return
+ */
+ protected boolean createFeaturesForAttributes(String attName,
+ List<String> residues)
+ {
+ boolean featureAdded = false;
+ String featureGroup = getViewerFeatureGroup();
+
+ for (String residue : residues)
+ {
+ AtomSpec spec = null;
+ String[] tokens = residue.split(" ");
+ if (tokens.length < 5)
+ {
+ continue;
+ }
+ String atomSpec = tokens[2];
+ String attValue = tokens[4];
+
+ /*
+ * ignore 'None' (e.g. for phi) or 'False' (e.g. for isHelix)
+ */
+ if ("None".equalsIgnoreCase(attValue)
+ || "False".equalsIgnoreCase(attValue))
+ {
+ continue;
+ }
+
+ try
+ {
+ spec = AtomSpec.fromChimeraAtomspec(atomSpec);
+ } catch (IllegalArgumentException e)
+ {
+ System.err.println("Problem parsing atomspec " + atomSpec);
+ continue;
+ }
+
+ String chainId = spec.getChain();
+ String description = attValue;
+ float score = Float.NaN;
+ try
+ {
+ score = Float.valueOf(attValue);
+ description = chainId;
+ } catch (NumberFormatException e)
+ {
+ // was not a float value
+ }
+
+ String pdbFile = getPdbFileForModel(spec.getModelNumber());
+ spec.setPdbFile(pdbFile);
+
+ List<AtomSpec> atoms = Collections.singletonList(spec);
+
+ /*
+ * locate the mapped position in the alignment (if any)
+ */
+ SearchResults sr = getSsm()
+ .findAlignmentPositionsForStructurePositions(atoms);
+
+ /*
+ * expect one matched alignment position, or none
+ * (if the structure position is not mapped)
+ */
+ for (SearchResultMatchI m : sr.getResults())
+ {
+ SequenceI seq = m.getSequence();
+ int start = m.getStart();
+ int end = m.getEnd();
+ SequenceFeature sf = new SequenceFeature(attName, description,
+ start, end, score, featureGroup);
+ // todo: should SequenceFeature have an explicit property for chain?
+ // note: repeating the action shouldn't duplicate features
+ featureAdded |= seq.addSequenceFeature(sf);
+ }
+ }
+ return featureAdded;
+ }
+
+ /**
+ * Answers the feature group name to apply to features created in Jalview from
+ * Chimera attributes
+ *
+ * @return
+ */
+ protected String getViewerFeatureGroup()
+ {
+ // todo pull up to interface
+ return CHIMERA_FEATURE_GROUP;
+ }
+
+
public Hashtable<String, String> getChainFile()
{
return chainFile;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.ChimeraCommands;
import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
import jalview.gui.StructureViewer.ViewerType;
- import jalview.io.AppletFormatAdapter;
+ import jalview.io.DataSourceType;
import jalview.io.JalviewFileChooser;
import jalview.io.JalviewFileView;
import jalview.io.StructureFile;
import jalview.ws.dbsources.Pdb;
import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import javax.swing.JColorChooser;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
+import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
// TODO Auto-generated method stub
}
});
+
+ JMenuItem writeFeatures = new JMenuItem(
+ MessageManager.getString("label.create_chimera_attributes"));
+ writeFeatures.setToolTipText(MessageManager
+ .getString("label.create_chimera_attributes_tip"));
+ writeFeatures.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ sendFeaturesToChimera();
+ }
+ });
+ viewerActionMenu.add(writeFeatures);
+
+ final JMenu fetchAttributes = new JMenu("Fetch Chimera attributes");
+ fetchAttributes
+ .setToolTipText("Copy Chimera attribute to Jalview feature");
+ fetchAttributes.addMouseListener(new MouseAdapter()
+ {
+
+ @Override
+ public void mouseEntered(MouseEvent e)
+ {
+ buildAttributesMenu(fetchAttributes);
+ }
+ });
+ viewerActionMenu.add(fetchAttributes);
+
+ }
+
+ /**
+ * Query Chimera for its residue attribute names and add them as items off the
+ * attributes menu
+ *
+ * @param attributesMenu
+ */
+ protected void buildAttributesMenu(JMenu attributesMenu)
+ {
+ List<String> atts = jmb.sendChimeraCommand("list resattr", true);
+ if (atts == null)
+ {
+ return;
+ }
+ attributesMenu.removeAll();
+ Collections.sort(atts);
+ for (String att : atts)
+ {
+ final String attName = att.split(" ")[1];
+
+ /*
+ * ignore 'jv_*' attributes, as these are Jalview features that have
+ * been transferred to residue attributes in Chimera!
+ */
+ if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX))
+ {
+ JMenuItem menuItem = new JMenuItem(attName);
+ menuItem.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ getChimeraAttributes(attName);
+ }
+ });
+ attributesMenu.add(menuItem);
+ }
+ }
+ }
+
+ /**
+ * Read residues in Chimera with the given attribute name, and set as features
+ * on the corresponding sequence positions (if any)
+ *
+ * @param attName
+ */
+ protected void getChimeraAttributes(String attName)
+ {
+ jmb.copyStructureAttributesToFeatures(attName, getAlignmentPanel());
+ }
+
+ /**
+ * Send a command to Chimera to create residue attributes for Jalview features
+ * <p>
+ * The syntax is: setattr r <attName> <attValue> <atomSpec>
+ * <p>
+ * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A
+ */
+ protected void sendFeaturesToChimera()
+ {
+ jmb.sendFeaturesToViewer(getAlignmentPanel());
}
/**
SequenceI[][] seqs)
{
createProgressBar();
- // FIXME extractChains needs pdbentries to match IDs to PDBEntry(s) on seqs
jmb = new JalviewChimeraBindingModel(this,
ap.getStructureSelectionManager(), pdbentrys, seqs, null);
addAlignmentPanel(ap);
}
-
-
/**
* Create a new viewer from saved session state data including Chimera session
* file
*/
void initChimera()
{
- jmb.setFinishedInit(false);
- jalview.gui.Desktop.addInternalFrame(this,
- jmb.getViewerTitle("Chimera", true), getBounds().width,
- getBounds().height);
+ Desktop.addInternalFrame(this, jmb.getViewerTitle("Chimera", true),
+ getBounds().width, getBounds().height);
if (!jmb.launchChimera())
{
- JOptionPane.showMessageDialog(Desktop.desktop,
+ JvOptionPane.showMessageDialog(Desktop.desktop,
MessageManager.getString("label.chimera_failed"),
MessageManager.getString("label.error_loading_file"),
- JOptionPane.ERROR_MESSAGE);
+ JvOptionPane.ERROR_MESSAGE);
this.dispose();
return;
}
+ chimeraSessionFile);
}
}
- jmb.setFinishedInit(true);
jmb.startChimeraListener();
}
+ /**
+ * If the list is not empty, add menu items for 'All' and each individual
+ * chain to the "View | Show Chain" sub-menu. Multiple selections are allowed.
+ *
+ * @param chainNames
+ */
+ @Override
+ void setChainMenuItems(List<String> chainNames)
+ {
+ chainMenu.removeAll();
+ if (chainNames == null || chainNames.isEmpty())
+ {
+ return;
+ }
+ JMenuItem menuItem = new JMenuItem(
+ MessageManager.getString("label.all"));
+ menuItem.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent evt)
+ {
+ allChainsSelected = true;
+ for (int i = 0; i < chainMenu.getItemCount(); i++)
+ {
+ if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
+ {
+ ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
+ }
+ }
+ showSelectedChains();
+ allChainsSelected = false;
+ }
+ });
+
+ chainMenu.add(menuItem);
+
+ for (String chainName : chainNames)
+ {
+ menuItem = new JCheckBoxMenuItem(chainName, true);
+ menuItem.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent evt)
+ {
+ if (!allChainsSelected)
+ {
+ showSelectedChains();
+ }
+ }
+ });
+
+ chainMenu.add(menuItem);
+ }
+ }
/**
* Show only the selected chain(s) in the viewer
"label.confirm_close_chimera",
new Object[] { jmb.getViewerTitle("Chimera", false) });
prompt = JvSwingUtils.wrapTooltip(true, prompt);
- int confirm = JOptionPane.showConfirmDialog(this, prompt,
+ int confirm = JvOptionPane.showConfirmDialog(this, prompt,
MessageManager.getString("label.close_viewer"),
- JOptionPane.YES_NO_CANCEL_OPTION);
+ JvOptionPane.YES_NO_CANCEL_OPTION);
/*
* abort closure if user hits escape or Cancel
*/
- if (confirm == JOptionPane.CANCEL_OPTION
- || confirm == JOptionPane.CLOSED_OPTION)
+ if (confirm == JvOptionPane.CANCEL_OPTION
+ || confirm == JvOptionPane.CLOSED_OPTION)
{
return;
}
- closeChimera = confirm == JOptionPane.YES_OPTION;
+ closeChimera = confirm == JvOptionPane.YES_OPTION;
}
jmb.closeViewer(closeChimera);
}
if (errormsgs.length() > 0)
{
- JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
+ JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
.formatMessage("label.pdb_entries_couldnt_be_retrieved",
new Object[] { errormsgs.toString() }),
MessageManager.getString("label.couldnt_load_file"),
- JOptionPane.ERROR_MESSAGE);
+ JvOptionPane.ERROR_MESSAGE);
}
if (files.length() > 0)
{
+ jmb.setFinishedInit(false);
if (!addingStructures)
{
try
jmb.openFile(pe);
jmb.addSequence(pos, jmb.getSequence()[pos]);
File fl = new File(pe.getFile());
- String protocol = AppletFormatAdapter.URL;
+ DataSourceType protocol = DataSourceType.URL;
try
{
if (fl.exists())
{
- protocol = AppletFormatAdapter.FILE;
+ protocol = DataSourceType.FILE;
}
} catch (Throwable e)
{
}
}
}
+
jmb.refreshGUI();
jmb.setFinishedInit(true);
jmb.setLoadingFromArchive(false);
setChainMenuItems(jmb.getChainNames());
this.setTitle(jmb.getViewerTitle("Chimera", true));
- if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
- {
+ // if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
+ // {
viewerActionMenu.setVisible(true);
- }
+ // }
if (!jmb.isLoadingFromArchive())
{
seqColour_actionPerformed(null);
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
+ import jalview.io.DataSourceType;
import jalview.structure.StructureSelectionManager;
public class JalviewChimeraBindingModel extends JalviewChimeraBinding
public JalviewChimeraBindingModel(ChimeraViewFrame chimeraViewFrame,
StructureSelectionManager ssm, PDBEntry[] pdbentry,
- SequenceI[][] sequenceIs, String protocol)
+ SequenceI[][] sequenceIs, DataSourceType protocol)
{
super(ssm, pdbentry, sequenceIs, protocol);
cvf = chimeraViewFrame;
@Override
public void run()
{
- long stm = cvf.startProgressBar(progressMsg);
+ long handle = 0;
+ if (progressMsg != null)
+ {
+ handle = cvf.startProgressBar(progressMsg);
+ }
try
{
sendChimeraCommand(command, false);
} finally
{
- cvf.stopProgressBar(null, stm);
+ if (progressMsg != null)
+ {
+ cvf.stopProgressBar(null, handle);
+ }
}
}
});
import jalview.datamodel.SequenceI;
import jalview.ext.jmol.JmolParser;
import jalview.gui.IProgressIndicator;
- import jalview.io.AppletFormatAdapter;
+ import jalview.io.DataSourceType;
import jalview.io.StructureFile;
import jalview.util.MappingUtils;
import jalview.util.MessageManager;
* @return null or the structure data parsed as a pdb file
*/
synchronized public StructureFile setMapping(SequenceI[] sequence,
- String[] targetChains, String pdbFile, String protocol)
+ String[] targetChains, String pdbFile, DataSourceType protocol)
{
return setMapping(true, sequence, targetChains, pdbFile, protocol);
}
* (may be nill, individual elements may be nill)
* @param pdbFile
* - structure data resource
- * @param protocol
+ * @param sourceType
* - how to resolve data from resource
* @return null or the structure data parsed as a pdb file
*/
synchronized public StructureFile setMapping(boolean forStructureView,
SequenceI[] sequenceArray, String[] targetChainIds,
- String pdbFile, String protocol)
+ String pdbFile, DataSourceType sourceType)
{
/*
* There will be better ways of doing this in the future, for now we'll use
boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
try
{
- pdb = new JmolParser(pdbFile, protocol);
+ pdb = new JmolParser(pdbFile, sourceType);
if (pdb.getId() != null && pdb.getId().trim().length() > 0
- && AppletFormatAdapter.FILE.equals(protocol))
+ && DataSourceType.FILE == sourceType)
{
registerPDBFile(pdb.getId().trim(), pdbFile);
}
continue;
}
- if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
+ if (sourceType == DataSourceType.PASTE)
{
pdbFile = "INLINE" + pdb.getId();
}
- ArrayList<StructureMapping> seqToStrucMapping = new ArrayList<StructureMapping>();
+ List<StructureMapping> seqToStrucMapping = new ArrayList<StructureMapping>();
if (isMapUsingSIFTs && seq.isProtein())
{
setProgressBar(null);
}
else
{
- ArrayList<StructureMapping> foundSiftsMappings = new ArrayList<StructureMapping>();
+ List<StructureMapping> foundSiftsMappings = new ArrayList<StructureMapping>();
for (PDBChain chain : pdb.getChains())
{
try
return;
}
- SearchResultsI results = new SearchResults();
+ SearchResultsI results = findAlignmentPositionsForStructurePositions(atoms);
+ for (Object li : listeners)
+ {
+ if (li instanceof SequenceListener)
+ {
+ ((SequenceListener) li).highlightSequence(results);
+ }
+ }
+ }
+
+ /**
+ * Constructs a SearchResults object holding regions (if any) in the Jalview
+ * alignment which have a mapping to the structure viewer positions in the
+ * supplied list
+ *
+ * @param atoms
+ * @return
+ */
+ public SearchResults findAlignmentPositionsForStructurePositions(
+ List<AtomSpec> atoms)
+ {
+ SearchResults results = new SearchResults();
for (AtomSpec atom : atoms)
{
SequenceI lastseq = null;
}
}
}
- for (Object li : listeners)
- {
- if (li instanceof SequenceListener)
- {
- ((SequenceListener) li).highlightSequence(results);
- }
- }
+ return results;
}
/**
import jalview.api.structures.JalviewStructureDisplayI;
import jalview.bin.Cache;
+import jalview.bin.Jalview;
import jalview.datamodel.SequenceI;
import jalview.gui.AlignFrame;
+ import jalview.gui.JvOptionPane;
import jalview.gui.Preferences;
import jalview.gui.StructureViewer;
import jalview.gui.StructureViewer.ViewerType;
- import jalview.io.FormatAdapter;
+ import jalview.io.DataSourceType;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
public class JmolViewerTest
{
+ @BeforeClass(alwaysRun = true)
+ public void setUpJvOptionPane()
+ {
+ JvOptionPane.setInteractiveMode(false);
+ JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+ }
+
/**
* @throws java.lang.Exception
*/
@BeforeClass(alwaysRun = true)
public static void setUpBeforeClass() throws Exception
{
- jalview.bin.Jalview.main(new String[] {
- "-noquestionnaire -nonews -props",
+ Jalview.main(new String[] { "-noquestionnaire", "-nonews", "-props",
"test/jalview/ext/rbvi/chimera/testProps.jvprops" });
}
Cache.setProperty(Preferences.STRUCTURE_DISPLAY, ViewerType.JMOL.name());
String inFile = "examples/1gaq.txt";
AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
- inFile, FormatAdapter.FILE);
+ inFile, DataSourceType.FILE);
assertTrue("Didn't read input file " + inFile, af != null);
for (SequenceI sq : af.getViewport().getAlignment().getSequences())
{
*/
package jalview.ext.rbvi.chimera;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+ import jalview.gui.JvOptionPane;
+
import java.awt.Color;
-import java.util.Arrays;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class ChimeraCommandsTest
{
+
+ @BeforeClass(alwaysRun = true)
+ public void setUpJvOptionPane()
+ {
+ JvOptionPane.setInteractiveMode(false);
+ JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+ }
+
@Test(groups = { "Functional" })
public void testBuildColourCommands()
{
- Map<Color, Map<Integer, Map<String, List<int[]>>>> map = new LinkedHashMap<Color, Map<Integer, Map<String, List<int[]>>>>();
- ChimeraCommands.addColourRange(map, Color.blue, 0, 2, 5, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 0, 7, 7, "B");
- ChimeraCommands.addColourRange(map, Color.blue, 0, 9, 23, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 1, 1, 1, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 1, 4, 7, "B");
- ChimeraCommands.addColourRange(map, Color.yellow, 1, 8, 8, "A");
- ChimeraCommands.addColourRange(map, Color.yellow, 1, 3, 5, "A");
- ChimeraCommands.addColourRange(map, Color.red, 0, 3, 5, "A");
+ Map<Object, AtomSpecModel> map = new LinkedHashMap<Object, AtomSpecModel>();
+ ChimeraCommands.addRange(map, Color.blue, 0, 2, 5, "A");
+ ChimeraCommands.addRange(map, Color.blue, 0, 7, 7, "B");
+ ChimeraCommands.addRange(map, Color.blue, 0, 9, 23, "A");
+ ChimeraCommands.addRange(map, Color.blue, 1, 1, 1, "A");
+ ChimeraCommands.addRange(map, Color.blue, 1, 4, 7, "B");
+ ChimeraCommands.addRange(map, Color.yellow, 1, 8, 8, "A");
+ ChimeraCommands.addRange(map, Color.yellow, 1, 3, 5, "A");
+ ChimeraCommands.addRange(map, Color.red, 0, 3, 5, "A");
+ ChimeraCommands.addRange(map, Color.red, 0, 6, 9, "A");
// Colours should appear in the Chimera command in the order in which
- // they were added; within colour, by model, by chain, and positions as
- // added
+ // they were added; within colour, by model, by chain, ranges in start order
String command = ChimeraCommands.buildColourCommands(map).get(0);
assertEquals(
- "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:8.A,3-5.A; color #ff0000 #0:3-5.A",
- command);
+ command,
+ "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:3-5.A,8.A; color #ff0000 #0:3-9.A");
+ }
+
+ @Test(groups = { "Functional" })
+ public void testBuildSetAttributeCommands()
+ {
+ /*
+ * make a map of { featureType, {featureValue, {residue range specification } } }
+ */
+ Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
+ Map<Object, AtomSpecModel> featureValues = new HashMap<Object, AtomSpecModel>();
+
+ /*
+ * start with just one feature/value...
+ */
+ featuresMap.put("chain", featureValues);
+ ChimeraCommands.addRange(featureValues, "X", 0, 8, 20, "A");
+
+ List<String> commands = ChimeraCommands
+ .buildSetAttributeCommands(featuresMap);
+ assertEquals(1, commands.size());
+
+ /*
+ * feature name gets a jv_ namespace prefix
+ * feature value is quoted in case it contains spaces
+ */
+ assertEquals(commands.get(0), "setattr r jv_chain \"X\" #0:8-20.A");
+
+ // add same feature value, overlapping range
+ ChimeraCommands.addRange(featureValues, "X", 0, 3, 9, "A");
+ // same feature value, contiguous range
+ ChimeraCommands.addRange(featureValues, "X", 0, 21, 25, "A");
+ commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+ assertEquals(1, commands.size());
+ assertEquals(commands.get(0), "setattr r jv_chain \"X\" #0:3-25.A");
+
+ // same feature value and model, different chain
+ ChimeraCommands.addRange(featureValues, "X", 0, 21, 25, "B");
+ // same feature value and chain, different model
+ ChimeraCommands.addRange(featureValues, "X", 1, 26, 30, "A");
+ commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+ assertEquals(1, commands.size());
+ assertEquals(commands.get(0),
+ "setattr r jv_chain \"X\" #0:3-25.A,21-25.B|#1:26-30.A");
+
+ // same feature, different value
+ ChimeraCommands.addRange(featureValues, "Y", 0, 40, 50, "A");
+ commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+ assertEquals(2, commands.size());
+ // commands are ordered by feature type but not by value
+ // so use contains to test for the expected command:
+ assertTrue(commands
+ .contains("setattr r jv_chain \"X\" #0:3-25.A,21-25.B|#1:26-30.A"));
+ assertTrue(commands.contains("setattr r jv_chain \"Y\" #0:40-50.A"));
+
+ featuresMap.clear();
+ featureValues.clear();
+ featuresMap.put("side-chain binding!", featureValues);
+ ChimeraCommands.addRange(featureValues, "metal ion!", 0, 7, 15, "A");
+ // feature names are sanitised to change space or hyphen to underscore
+ commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+ assertTrue(commands
+ .contains("setattr r jv_side_chain_binding_ \"metal ion!\" #0:7-15.A"));
+ }
+
+ /**
+ * Tests for the method that prefixes and sanitises a feature name so it can
+ * be used as a valid, namespaced attribute name in Chimera
+ */
+ @Test(groups = { "Functional" })
+ public void testMakeAttributeName()
+ {
+ assertEquals(ChimeraCommands.makeAttributeName(null), "jv_");
+ assertEquals(ChimeraCommands.makeAttributeName(""), "jv_");
+ assertEquals(ChimeraCommands.makeAttributeName("helix"), "jv_helix");
+ assertEquals(ChimeraCommands.makeAttributeName("Hello World 24"),
+ "jv_Hello_World_24");
+ assertEquals(
+ ChimeraCommands.makeAttributeName("!this is-a_very*{odd(name"),
+ "jv__this_is_a_very__odd_name");
+ // name ending in color gets underscore appended
+ assertEquals(ChimeraCommands.makeAttributeName("helixColor"),
+ "jv_helixColor_");
}
}
*/
package jalview.ext.rbvi.chimera;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
+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.FileLoader;
- import jalview.io.FormatAdapter;
+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 jalview.io.DataSourceType;
import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
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.bin.Jalview.main(new String[] {
- "-noquestionnaire -nonews -props",
+ 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);
}
/**
@AfterClass(alwaysRun = true)
public static void tearDownAfterClass() throws Exception
{
- jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+ Desktop.instance.closeAll_actionPerformed(null);
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDownAfterTest() throws Exception
+ {
+ SiftsClient.setMockSiftsFile(null);
+ if (chimeraViewer != null)
+ {
+ chimeraViewer.closeViewer(true);
+ }
}
- @Test(groups = { "Functional" })
+ /**
+ * 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()
{
- Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
- ViewerType.CHIMERA.name());
String inFile = "examples/1gaq.txt";
- AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
- inFile, DataSourceType.FILE);
- assertTrue("Didn't read input file " + inFile, af != null);
- for (SequenceI sq : af.getViewport().getAlignment().getSequences())
+ AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
- FormatAdapter.FILE);
++ 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<PDBEntry> 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())
{
- System.out.println("** sq=" + sq.getName());
- SequenceI dsq = sq.getDatasetSequence();
- while (dsq.getDatasetSequence() != null)
+ try
+ {
+ Thread.sleep(500);
+ } catch (InterruptedException e)
{
- dsq = dsq.getDatasetSequence();
}
- if (dsq.getAllPDBEntries() != null
- && dsq.getAllPDBEntries().size() > 0)
+ }
+
+ 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,
- FormatAdapter.FILE);
++ 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.getPdbFile()[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<String> 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,
- FormatAdapter.FILE);
++ 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)
{
- for (int q = 0; q < dsq.getAllPDBEntries().size(); q++)
- {
- final StructureViewer structureViewer = new StructureViewer(af
- .getViewport().getStructureSelectionManager());
- structureViewer.setViewerType(ViewerType.CHIMERA);
- JalviewStructureDisplayI chimeraViewer = structureViewer
- .viewStructures(dsq.getAllPDBEntries().elementAt(q),
- new SequenceI[] { sq }, af.getCurrentView()
- .getAlignPanel());
- /*
- * Wait for viewer load thread to complete
- */
- while (!chimeraViewer.getBinding().isFinishedInit())
- {
- try
- {
- Thread.sleep(500);
- } catch (InterruptedException e)
- {
- }
- }
- assertEquals(1, chimeraViewer.getBinding().getPdbCount());
- chimeraViewer.closeViewer(true);
- // todo: break here means only once through this loop?
- break;
- }
- break;
}
+ } 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<SequenceFeature> fs = fr.findFeaturesAtRes(fer2Arath, 54);
+ assertEquals(fs.size(), 3);
+ assertEquals(fs.get(0).getType(), "RESNUM");
+ assertEquals(fs.get(1).getType(), "phi");
+ assertEquals(fs.get(2).getType(), "phi");
+ assertEquals(fs.get(1).getDescription(), "A"); // chain
+ assertEquals(fs.get(2).getDescription(), "B");
+ assertEquals(fs.get(1).getScore(), -131.0713f, 0.001f);
+ assertEquals(fs.get(2).getScore(), -127.39512, 0.001f);
+
+ /*
+ * 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<SequenceFeature> fs = fr.findFeaturesAtRes(seq, 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);
}
}