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.schemes.ColourSchemeI;
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;
import java.util.Map;
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 static final String ALPHACARBON = "CA";
+ private List<String> chainNames = new ArrayList<String>();
+
+ private Hashtable<String, String> chainFile = new Hashtable<String, String>();
+
/*
* Object through which we talk to Chimera
*/
*/
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 ssm
* @param pdbentry
* @param sequenceIs
- * @param chains
* @param protocol
*/
public JalviewChimeraBinding(StructureSelectionManager ssm,
- PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
- String protocol)
+ PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
{
- super(ssm, pdbentry, sequenceIs, chains, protocol);
+ super(ssm, pdbentry, sequenceIs, protocol);
viewer = new ChimeraManager(
new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
}
boolean first = true;
for (String chain : toshow)
{
+ int modelNumber = getModelNoForChain(chain);
+ String showChainCmd = modelNumber == -1 ? "" : modelNumber + ":."
+ + chain.split(":")[1];
if (!first)
{
cmd.append(",");
}
- cmd.append(":.").append(chain);
+ cmd.append(showChainCmd);
first = false;
}
* window, but it looks more helpful not to (easier to relate chains to the
* whole)
*/
- final String command = "~display #*; ~ribbon #*; ribbon "
+ final String command = "~display #*; ~ribbon #*; ribbon :"
+ cmd.toString();
sendChimeraCommand(command, false);
}
chimeraListener.shutdown();
chimeraListener = null;
}
- lastCommand = null;
viewer = null;
releaseUIResources();
}
/**
- * Send a command to Chimera, and optionally log and return 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 getResponse
}
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(),
}
}
viewerCommandHistory(true);
- lastCommand = command;
return reply;
}
*/
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
{
return new String[0];
}
- // if (modelFileNames == null)
- // {
- // Collection<ChimeraModel> chimodels = viewer.getChimeraModels();
- // _modelFileNameMap = new int[chimodels.size()];
- // int j = 0;
- // for (ChimeraModel chimodel : chimodels)
- // {
- // String mdlName = chimodel.getModelName();
- // }
- // modelFileNames = new String[j];
- // // System.arraycopy(mset, 0, modelFileNames, 0, j);
- // }
return chimeraMaps.keySet().toArray(
modelFileNames = new String[chimeraMaps.size()]);
* 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()
{
- List<String> names = new ArrayList<String>();
- String[][] allNames = getChains();
- if (allNames != null)
- {
- for (String[] chainsForPdb : allNames)
- {
- if (chainsForPdb != null)
- {
- for (String chain : chainsForPdb)
- {
- if (chain != null && !names.contains(chain))
- {
- names.add(chain);
- }
- }
- }
- }
- }
- return names;
+ return chainNames;
}
/**
*
* @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);
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);
+ }
+ }
}
/**
+ 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;
+ }
+
+ public List<ChimeraModel> getChimeraModelByChain(String chain)
+ {
+ return chimeraMaps.get(chainFile.get(chain));
+ }
+
+ public int getModelNoForChain(String chain)
+ {
+ List<ChimeraModel> foundModels = getChimeraModelByChain(chain);
+ if (foundModels != null && !foundModels.isEmpty())
+ {
+ return foundModels.get(0).getModelNumber();
+ }
+ return -1;
+ }
}