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 java.net.BindException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
+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
*/
* @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);
}
*/
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()]);
List<AtomSpec> atomSpecs = new ArrayList<AtomSpec>();
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[0];
- 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));
}
- return 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;
}
/**
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;
+ // 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);
- /*
- * 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();
+ 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();
- /*
- * Expect 0, 1 or more reply lines of the format (chi2 is attName):
- * residue id #0:5.A chi2 -155.000836316 index 5
- */
- Map<String, Map<SequenceI, List<Integer>>> attsMap = new HashMap<String, Map<SequenceI, List<Integer>>>();
for (String residue : residues)
{
+ AtomSpec spec = null;
String[] tokens = residue.split(" ");
- if (tokens.length > 4)
+ if (tokens.length < 5)
{
- String atomSpec = tokens[2];
- String attValue = tokens[4];
- // TODO find mapping of atomspec to Jalview residue if any
- // build a map of { attValue, Map<SequenceI, List<Integer>> }
- // 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
+ continue;
+ }
+ String atomSpec = tokens[2];
+ String attValue = tokens[4];
- /*
- * record attribute value / sequence / sequence position
- */
- Map<SequenceI, List<Integer>> seqMap = attsMap.get(attValue);
- if (seqMap == null)
- {
- seqMap = new HashMap<SequenceI, List<Integer>>();
- attsMap.put(attValue, seqMap);
- }
- List<Integer> seqPositions = seqMap.get(seq);
- if (seqPositions == null)
- {
- seqPositions = new ArrayList<Integer>();
- seqMap.put(seq, seqPositions);
- }
- seqPositions.add(seqPos);
+ /*
+ * ignore 'None' (e.g. for phi) or 'False' (e.g. for isHelix)
+ */
+ if ("None".equalsIgnoreCase(attValue)
+ || "False".equalsIgnoreCase(attValue))
+ {
+ continue;
}
- }
- /*
- * traverse values and sequences
- */
- for (String val : attsMap.keySet())
- {
- for (SequenceI seq : attsMap.get(val).keySet())
+ try
{
- List<Integer> positions = attsMap.get(val).get(seq);
- Collections.sort(positions);
- // TODO find reusable code that compacts the list
- List<int[]> 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);
- }
+ 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;
}
/**
protected String getViewerFeatureGroup()
{
// todo pull up to interface
- return "Chimera";
+ 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;
}
}