From b557f2f2a0bef9d5d4a10cfd0731562e423531db Mon Sep 17 00:00:00 2001 From: gmungoc Date: Mon, 14 Nov 2016 09:43:28 +0000 Subject: [PATCH] JAL-2295 JAL-2296 skeleton 'attributes to features'; renamed methods with a view to pull-up --- .../edu/ucsf/rbvi/strucviz2/ChimeraManager.java | 5 + src/jalview/ext/rbvi/chimera/ChimeraCommands.java | 2 +- src/jalview/ext/rbvi/chimera/ChimeraListener.java | 2 +- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 176 +++++++++++++++++--- src/jalview/gui/ChimeraViewFrame.java | 72 +++++++- src/jalview/structure/AtomSpec.java | 3 - .../ext/rbvi/chimera/JalviewChimeraView.java | 2 +- 7 files changed, 229 insertions(+), 33 deletions(-) diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java index fdcf34f..21ae914 100644 --- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java +++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java @@ -389,6 +389,11 @@ public class ChimeraManager "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+"); diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index 3415815..7020f90 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -49,7 +49,7 @@ import MCview.PDBChain; public class ChimeraCommands { - private static final String NAMESPACE_PREFIX = "jv_"; + public static final String NAMESPACE_PREFIX = "jv_"; /** * Constructs Chimera commands to colour residues as per the Jalview alignment diff --git a/src/jalview/ext/rbvi/chimera/ChimeraListener.java b/src/jalview/ext/rbvi/chimera/ChimeraListener.java index ba5e332..507ddbc 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraListener.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraListener.java @@ -129,7 +129,7 @@ public class ChimeraListener extends AbstractRequestHandler implements } /** - * Handler a ModelChanged notification from Chimera + * Handle a ModelChanged notification from Chimera * * @param substring */ diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 3d6e239..a74b3ab 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -27,6 +27,7 @@ import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.httpserver.AbstractRequestHandler; import jalview.schemes.ColourSchemeI; @@ -44,6 +45,8 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.BindException; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -97,14 +100,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel */ private Map> chimeraMaps = new LinkedHashMap>(); - /* - * 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; /* @@ -282,7 +277,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel chimeraListener.shutdown(); chimeraListener = null; } - lastCommand = null; viewer = null; releaseUIResources(); @@ -596,7 +590,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } /** - * Send a command to Chimera, and optionally log and return any responses + * Send a command to Chimera, and optionally log and return any responses. + *

+ * Does nothing, and returns null, if the command is the same as the last one + * sent [why?]. * * @param command * @param getResponse @@ -611,7 +608,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } List 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 lastReply = viewer.sendChimeraCommand(command.trim(), @@ -627,7 +624,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } } viewerCommandHistory(true); - lastCommand = command; return reply; } @@ -864,8 +860,27 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel * 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 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 convertStructureResiduesToAlignment( + List structureSelection) + { List atomSpecs = new ArrayList(); - for (String atomSpec : selection) + for (String atomSpec : structureSelection) { int colonPos = atomSpec.indexOf(":"); if (colonPos == -1) @@ -897,7 +912,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel /* * Work out the pdbfilename from the model number */ - String pdbfilename = modelFileNames[frameNo]; + String pdbfilename = modelFileNames[0]; findfileloop: for (String pdbfile : this.chimeraMaps.keySet()) { for (ChimeraModel cm : chimeraMaps.get(pdbfile)) @@ -911,12 +926,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } 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 atomSpecs; } private void log(String message) @@ -1129,8 +1139,9 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel * * @param avp */ - public void sendFeaturesToChimera(AlignmentViewPanel avp) + public void sendFeaturesToViewer(AlignmentViewPanel avp) { + // TODO refactor as required to pull up to an interface AlignmentI alignment = avp.getAlignment(); FeatureRenderer fr = getFeatureRenderer(avp); @@ -1151,11 +1162,18 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel StructureMappingcommandSet commandSet = ChimeraCommands .getSetAttributeCommandsForFeatures(getSsm(), files, getSequence(), fr, alignment); - // for (String command : commandSet.commands) - // { - // sendAsynchronousCommand(command, null); - // } - sendCommandsByFile(commandSet.commands); + String[] commands = commandSet.commands; + if (commands.length > 10) + { + sendCommandsByFile(commands); + } + else + { + for (String command : commands) + { + sendAsynchronousCommand(command, null); + } + } } /** @@ -1187,4 +1205,110 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel + 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 + */ + String cmd = "list residues spec ':*/" + attName + "' attr " + attName; + List residues = sendChimeraCommand(cmd, true); + + /* + * TODO check if Jalview already has this feature name, if so give it a + * distinguishing prefix e.g. chim_ + */ + FeatureRenderer fr = alignmentPanel.getFeatureRenderer(); + fr.getFeaturesDisplayed().getVisibleFeatureCount(); + + /* + * Expect 0, 1 or more reply lines of the format (chi2 is attName): + * residue id #0:5.A chi2 -155.000836316 index 5 + */ + Map>> attsMap = new HashMap>>(); + for (String residue : residues) + { + String[] tokens = residue.split(" "); + if (tokens.length > 4) + { + String atomSpec = tokens[2]; + String attValue = tokens[4]; + // TODO find mapping of atomspec to Jalview residue if any + // build a map of { attValue, Map> } + // sort the integer lists + // and create a feature for each contiguous integer range + SequenceI seq = null; // mapped-to sequence + int seqPos = 0; // mapped-to sequence position + + /* + * record attribute value / sequence / sequence position + */ + Map> seqMap = attsMap.get(attValue); + if (seqMap == null) + { + seqMap = new HashMap>(); + attsMap.put(attValue, seqMap); + } + List seqPositions = seqMap.get(seq); + if (seqPositions == null) + { + seqPositions = new ArrayList(); + seqMap.put(seq, seqPositions); + } + seqPositions.add(seqPos); + } + } + + /* + * traverse values and sequences + */ + for (String val : attsMap.keySet()) + { + for (SequenceI seq : attsMap.get(val).keySet()) + { + List positions = attsMap.get(val).get(seq); + Collections.sort(positions); + // TODO find reusable code that compacts the list + List ranges = null;// compacted list + for (int[] range : ranges) + { + float score = Float.NaN; + try + { + score = Float.valueOf(val); + } catch (NumberFormatException e) + { + // was not a float value + } + String featureGroup = getViewerFeatureGroup(); + SequenceFeature sf = new SequenceFeature(attName, val, range[0], + range[1], score, featureGroup); + // note: repeating the action shouldn't duplicate features + seq.addSequenceFeature(sf); + } + } + } + } + + /** + * 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"; + } } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index d68cce8..d43702c 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -26,6 +26,7 @@ import jalview.datamodel.AlignmentI; 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; @@ -49,6 +50,8 @@ 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; @@ -58,6 +61,7 @@ import java.io.IOException; 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; @@ -181,6 +185,7 @@ public class ChimeraViewFrame extends StructureViewerBase // TODO Auto-generated method stub } }); + JMenuItem writeFeatures = new JMenuItem( MessageManager.getString("label.create_chimera_attributes")); writeFeatures.setToolTipText(MessageManager @@ -194,6 +199,71 @@ public class ChimeraViewFrame extends StructureViewerBase } }); 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 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()); } /** @@ -205,7 +275,7 @@ public class ChimeraViewFrame extends StructureViewerBase */ protected void sendFeaturesToChimera() { - jmb.sendFeaturesToChimera(getAlignmentPanel()); + jmb.sendFeaturesToViewer(getAlignmentPanel()); } /** diff --git a/src/jalview/structure/AtomSpec.java b/src/jalview/structure/AtomSpec.java index a19acef..271bf1d 100644 --- a/src/jalview/structure/AtomSpec.java +++ b/src/jalview/structure/AtomSpec.java @@ -29,9 +29,6 @@ package jalview.structure; */ public class AtomSpec { - // TODO clarify do we want pdbFile here, or pdbId? - // compare highlightAtom in 2.8.2 for JalviewJmolBinding and - // javascript.MouseOverStructureListener private String pdbFile; private String chain; diff --git a/test/jalview/ext/rbvi/chimera/JalviewChimeraView.java b/test/jalview/ext/rbvi/chimera/JalviewChimeraView.java index 6ea377f..1b7d9d0 100644 --- a/test/jalview/ext/rbvi/chimera/JalviewChimeraView.java +++ b/test/jalview/ext/rbvi/chimera/JalviewChimeraView.java @@ -264,7 +264,7 @@ public class JalviewChimeraView */ // TODO rename and pull up method to binding interface // once functionality is added for Jmol as well - binding.sendFeaturesToChimera(af.getViewport().getAlignPanel()); + binding.sendFeaturesToViewer(af.getViewport().getAlignPanel()); /* * give Chimera time to open the commands file and execute it -- 1.7.10.2