From 0e9b7c55df4d5d513d55de806dae32175ab22c84 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Mon, 14 Nov 2016 12:31:30 +0000 Subject: [PATCH] JAL-2296 new code and refactoring for 'copy Chimera attribute to features' --- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 209 +++++++++++--------- .../structure/StructureSelectionManager.java | 29 ++- 2 files changed, 139 insertions(+), 99 deletions(-) diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index a74b3ab..54adf87 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -27,6 +27,8 @@ import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; +import jalview.datamodel.SearchResults; +import jalview.datamodel.SearchResults.Match; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.httpserver.AbstractRequestHandler; @@ -46,7 +48,6 @@ 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; @@ -58,6 +59,10 @@ import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType; public abstract class JalviewChimeraBinding extends AAStructureBindingModel { + public static final String CHIMERA_FEATURE_GROUP = "Chimera"; + + private static final String CHIMERA_FEATURE_PREFIX = "chim_"; + // Chimera clause to exclude alternate locations in atom selection private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9"; @@ -224,7 +229,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel */ public String getViewerTitle(boolean verbose) { - return getViewerTitle("Chimera", verbose); + return getViewerTitle(CHIMERA_FEATURE_GROUP, verbose); } /** @@ -882,51 +887,42 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel List atomSpecs = new ArrayList(); 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) @@ -1217,11 +1213,16 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel 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 residues = sendChimeraCommand(cmd, true); /* @@ -1229,78 +1230,102 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel * distinguishing prefix e.g. chim_ */ FeatureRenderer fr = alignmentPanel.getFeatureRenderer(); - fr.getFeaturesDisplayed().getVisibleFeatureCount(); + // todo should getRenderOrder be in api.FeatureRenderer? + // FIXME this is empty if feature display is turned off + List existingFeatures = ((jalview.gui.FeatureRenderer) fr) + .getRenderOrder(); + if (existingFeatures.contains(attName)) + { + attName = getStructureFeaturePrefix() + attName; + } /* * Expect 0, 1 or more reply lines of the format (chi2 is attName): * residue id #0:5.A chi2 -155.000836316 index 5 + * or + * residue id #0:6.A chi3 None + * + * We assume here that attributes on structure do not naturally convert + * to ranges on sequence, i.e. we just set one feature per mapped position. + * + * To conflate positions, would need to first build a map + * Map>> + * and then traverse it to find feature ranges. */ - Map>> attsMap = new HashMap>>(); + boolean featureAdded = false; for (String residue : residues) { + AtomSpec spec = null; String[] tokens = residue.split(" "); - if (tokens.length > 4) + if (tokens.length < 5) + { + continue; + } + String atomSpec = tokens[2]; + String attValue = tokens[4]; + if ("None".equals(attValue)) { - 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 + continue; + } + try + { + spec = AtomSpec.fromChimeraAtomspec(atomSpec); + } catch (IllegalArgumentException e) + { + System.err.println("Problem parsing atomspec " + atomSpec); + continue; + } + String pdbFile = getPdbFileForModel(spec.getModelNumber()); + spec.setPdbFile(pdbFile); - /* - * record attribute value / sequence / sequence position - */ - Map> seqMap = attsMap.get(attValue); - if (seqMap == null) + List atoms = Collections.singletonList(spec); + SearchResults sr = getSsm() + .findAlignmentPositionsForStructurePositions(atoms); + + /* + * expect one matched alignment position, or none + * (if the structure position is not mapped) + */ + for (Match m : sr.getResults()) + { + SequenceI seq = m.getSequence(); + int start = m.getStart(); + int end = m.getEnd(); + + float score = Float.NaN; + try { - seqMap = new HashMap>(); - attsMap.put(attValue, seqMap); - } - List seqPositions = seqMap.get(seq); - if (seqPositions == null) + score = Float.valueOf(attValue); + } catch (NumberFormatException e) { - seqPositions = new ArrayList(); - seqMap.put(seq, seqPositions); + // was not a float value } - seqPositions.add(seqPos); + String featureGroup = getViewerFeatureGroup(); + SequenceFeature sf = new SequenceFeature(attName, attValue, start, + end, score, featureGroup); + // note: repeating the action shouldn't duplicate features + featureAdded |= seq.addSequenceFeature(sf); } } - - /* - * traverse values and sequences - */ - for (String val : attsMap.keySet()) + if (featureAdded) { - 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); - } - } + fr.featuresAdded(); } } /** + * Answers a 'namespace' prefix to use for features created in Jalview from + * attributes in the structure viewer + * + * @return + */ + protected String getStructureFeaturePrefix() + { + // TODO pull up as abstract + return CHIMERA_FEATURE_PREFIX; + } + + /** * Answers the feature group name to apply to features created in Jalview from * Chimera attributes * @@ -1309,6 +1334,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel protected String getViewerFeatureGroup() { // todo pull up to interface - return "Chimera"; + return CHIMERA_FEATURE_GROUP; } } diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index cad2303..c8708bd 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -805,6 +805,27 @@ public class StructureSelectionManager return; } + SearchResults 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 atoms) + { SearchResults results = new SearchResults(); for (AtomSpec atom : atoms) { @@ -830,13 +851,7 @@ public class StructureSelectionManager } } } - for (Object li : listeners) - { - if (li instanceof SequenceListener) - { - ((SequenceListener) li).highlightSequence(results); - } - } + return results; } /** -- 1.7.10.2