From defb4c1d5b4edbd8fbd490f25c15ef1f1de6fe37 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Fri, 2 Aug 2019 16:51:22 +0100 Subject: [PATCH] JAL-3390 first draft of showing only visible alignment in Chimera --- src/jalview/ext/jmol/JalviewJmolBinding.java | 7 ++ src/jalview/ext/rbvi/chimera/ChimeraCommands.java | 28 ++--- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 132 ++++++++++++++++++-- src/jalview/gui/ChimeraViewFrame.java | 6 +- src/jalview/gui/StructureViewerBase.java | 12 ++ src/jalview/jbgui/GStructureViewer.java | 3 + .../structures/models/AAStructureBindingModel.java | 42 ++++++- 7 files changed, 198 insertions(+), 32 deletions(-) diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index c0a1e0d..762b08e 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -20,6 +20,7 @@ */ package jalview.ext.jmol; +import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; @@ -1416,4 +1417,10 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel { showConsole(false); } + + @Override + public void showStructures(AlignViewportI av) + { + // TODO show Jmol structure optionally restricted to visible alignment + } } diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index dad8511..ec94e7d 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -50,9 +50,11 @@ import java.util.Map; */ public class ChimeraCommands { - public static final String NAMESPACE_PREFIX = "jv_"; + private static final String COLOR_GRAY_HEX = "color " + + ColorUtils.toTkCode(Color.GRAY); + /** * Constructs Chimera commands to colour residues as per the Jalview alignment * @@ -104,19 +106,16 @@ public class ChimeraCommands * delimited). If length limit issues arise, refactor to return one color * command per colour. */ - List commands = new ArrayList(); + List commands = new ArrayList<>(); StringBuilder sb = new StringBuilder(256); - boolean firstColour = true; + sb.append(COLOR_GRAY_HEX); + for (Object key : colourMap.keySet()) { Color colour = (Color) key; String colourCode = ColorUtils.toTkCode(colour); - if (!firstColour) - { - sb.append("; "); - } + sb.append("; "); sb.append("color ").append(colourCode).append(" "); - firstColour = false; final AtomSpecModel colourData = colourMap.get(colour); sb.append(colourData.getAtomSpec()); } @@ -196,7 +195,7 @@ public class ChimeraCommands AlignViewportI viewport = viewPanel.getAlignViewport(); HiddenColumns cs = viewport.getAlignment().getHiddenColumns(); AlignmentI al = viewport.getAlignment(); - Map colourMap = new LinkedHashMap(); + Map colourMap = new LinkedHashMap<>(); Color lastColour = null; for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) @@ -236,11 +235,12 @@ public class ChimeraCommands Color colour = sr.getResidueColour(seq, r, finder); /* - * darker colour for hidden regions + * hidden regions are shown gray + * todo: iterate over visible columns only */ if (!cs.isVisible(r)) { - colour = Color.GRAY; + continue; // colour = Color.GRAY; } final String chain = mapping[m].getChain(); @@ -349,7 +349,7 @@ public class ChimeraCommands StructureSelectionManager ssm, String[] files, SequenceI[][] seqs, AlignmentViewPanel viewPanel) { - Map> theMap = new LinkedHashMap>(); + Map> theMap = new LinkedHashMap<>(); FeatureRenderer fr = viewPanel.getFeatureRenderer(); if (fr == null) @@ -445,7 +445,7 @@ public class ChimeraCommands Map featureValues = theMap.get(type); if (featureValues == null) { - featureValues = new HashMap(); + featureValues = new HashMap<>(); theMap.put(type, featureValues); } for (int[] range : mappedRanges) @@ -475,7 +475,7 @@ public class ChimeraCommands protected static List buildSetAttributeCommands( Map> featureMap) { - List commands = new ArrayList(); + List commands = new ArrayList<>(); for (String featureType : featureMap.keySet()) { String attributeName = makeAttributeName(featureType); diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 00446f2..6fa06d2 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -20,6 +20,7 @@ */ package jalview.ext.rbvi.chimera; +import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.SequenceRenderer; import jalview.api.structures.JalviewStructureDisplayI; @@ -31,11 +32,13 @@ import jalview.datamodel.SearchResultMatchI; import jalview.datamodel.SearchResultsI; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.datamodel.VisibleContigsIterator; import jalview.httpserver.AbstractRequestHandler; import jalview.io.DataSourceType; import jalview.schemes.ColourSchemeI; import jalview.schemes.ResidueProperties; import jalview.structure.AtomSpec; +import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; @@ -76,9 +79,9 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel private static final String ALPHACARBON = "CA"; - private List chainNames = new ArrayList(); + private List chainNames = new ArrayList<>(); - private Hashtable chainFile = new Hashtable(); + private Hashtable chainFile = new Hashtable<>(); /* * Object through which we talk to Chimera @@ -106,7 +109,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel /* * Map of ChimeraModel objects keyed by PDB full local file name */ - private Map> chimeraMaps = new LinkedHashMap>(); + private Map> chimeraMaps = new LinkedHashMap<>(); String lastHighlightCommand; @@ -133,7 +136,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel String file = pe.getFile(); try { - List modelsToMap = new ArrayList(); + List modelsToMap = new ArrayList<>(); List oldList = viewer.getModelList(); boolean alreadyOpen = false; @@ -519,8 +522,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel System.out.println( "Superimpose command(s):\n" + command.toString()); } - allComs.append("~display all; chain @CA|P; ribbon ") - .append(selectioncom.toString()) + allComs/*.append("~display all; chain @CA|P; ribbon ") + .append(selectioncom.toString())*/ .append(";" + command.toString()); } } @@ -537,13 +540,24 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { System.out.println("Select regions:\n" + selectioncom.toString()); } - allComs.append("; ~display all; chain @CA|P; ribbon ") - .append(selectioncom.toString()).append("; focus"); + allComs.append("; ~display "); // all"); + if (!isShowAlignmentOnly()) + { + allComs.append("; ribbon; chain @CA|P"); + } + else + { + allComs.append("; ~ribbon"); + } + allComs.append("; ribbon ").append(selectioncom.toString()) + .append("; focus"); List chimeraReplies = sendChimeraCommand(allComs.toString(), true); for (String reply : chimeraReplies) { - if (reply.toLowerCase().contains("unequal numbers of atoms")) + String lowerCase = reply.toLowerCase(); + if (lowerCase.contains("unequal numbers of atoms") + || lowerCase.contains("at least")) { error = reply; } @@ -857,7 +871,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel protected List convertStructureResiduesToAlignment( List structureSelection) { - List atomSpecs = new ArrayList(); + List atomSpecs = new ArrayList<>(); for (String atomSpec : structureSelection) { try @@ -1308,4 +1322,102 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } return -1; } + + @Override + public void showStructures(AlignViewportI av) + { + StringBuilder cmd = new StringBuilder(128); + cmd.append("~display; ~ribbon;"); + if (isShowAlignmentOnly()) + { + String atomSpec = getMappedResidues(av); + cmd.append("ribbon ").append(atomSpec); + } + else + { + cmd.append("chain @CA|P; ribbon"); + } + cmd.append("; focus"); + sendChimeraCommand(cmd.toString(), false); + } + + /** + * Builds a Chimera atomSpec of residues mapped from sequences, of the format + * (#model:residues.chain) + * + *
+   * #0:2-94.A | #1:1-93.C | #2:1-93.A
+   * 
+ * + * Only residues visible in the alignment are included, that is, hidden columns + * and sequences are excluded. + * + * @param av + * @return + */ + private String getMappedResidues(AlignViewportI av) + { + AlignmentI alignment = av.getAlignment(); + final int width = alignment.getWidth(); + + String[] files = getStructureFiles(); + + StringBuilder atomSpec = new StringBuilder(256); + + for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) + { + StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]); + + /* + * Find the first mapped sequence (if any) for this PDB entry which is in + * the alignment + */ + final int seqCountForPdbFile = getSequence()[pdbfnum].length; + for (int s = 0; s < seqCountForPdbFile; s++) + { + for (StructureMapping mapping : mappings) + { + final SequenceI theSequence = getSequence()[pdbfnum][s]; + if (mapping.getSequence() == theSequence + && alignment.findIndex(theSequence) > -1) + { + String chainCd = mapping.getChain(); + + // TODO only process sequence ranges within visible columns + VisibleContigsIterator visible = alignment.getHiddenColumns() + .getVisContigsIterator(0, width, true); + while (visible.hasNext()) + { + int[] visibleRegion = visible.next(); + int seqStartPos = theSequence.findPosition(visibleRegion[0]); + int seqEndPos = theSequence.findPosition(visibleRegion[1]); + List residueRanges = mapping + .getPDBResNumRanges(seqStartPos, seqEndPos); + if (!residueRanges.isEmpty()) + { + if (atomSpec.length() > 0) + { + atomSpec.append("| "); + } + atomSpec.append(getModelSpec(pdbfnum)).append(":"); + boolean first = true; + for (int[] range : residueRanges) + { + if (!first) + { + atomSpec.append(","); + } + first = false; + atomSpec.append(range[0]).append("-").append(range[1]); + atomSpec.append(".").append(chainCd); + } + } + } + } + } + } + } + + return atomSpec.toString(); + } } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index a7349b8..6c35a9c 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -834,10 +834,8 @@ public class ChimeraViewFrame extends StructureViewerBase protected String alignStructs_withAllAlignPanels() { String reply = super.alignStructs_withAllAlignPanels(); - if (reply != null) - { - statusBar.setText("Superposition failed: " + reply); - } + statusBar.setText( + reply == null ? " " : "Superposition failed: " + reply); return reply; } diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index 35a5475..5c50044 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -709,6 +709,18 @@ public abstract class StructureViewerBase extends GStructureViewer }); viewMenu.add(seqColourBy); + showAlignmentOnly = new JCheckBoxMenuItem("Visible alignment only"); + showAlignmentOnly.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + getBinding().setShowAlignmentOnly(showAlignmentOnly.isSelected()); + getBinding().showStructures(getAlignmentPanel().getAlignViewport()); + } + }); + viewMenu.add(showAlignmentOnly); + final ItemListener handler = new ItemListener() { @Override diff --git a/src/jalview/jbgui/GStructureViewer.java b/src/jalview/jbgui/GStructureViewer.java index 83d8590..2094201 100644 --- a/src/jalview/jbgui/GStructureViewer.java +++ b/src/jalview/jbgui/GStructureViewer.java @@ -29,6 +29,7 @@ import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.JMenu; @@ -54,6 +55,8 @@ public abstract class GStructureViewer extends JInternalFrame protected JMenuItem alignStructs; + protected JCheckBoxMenuItem showAlignmentOnly; + protected JMenuItem fitToWindow; protected JRadioButtonMenuItem seqColour; diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index 2528286..21b0e3d 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -20,6 +20,7 @@ */ package jalview.structures.models; +import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.SequenceRenderer; import jalview.api.StructureSelectionManagerProvider; @@ -94,6 +95,8 @@ public abstract class AAStructureBindingModel public String fileLoadingError; + private boolean showAlignmentOnly; + /** * Data bean class to simplify parameterisation in superposeStructures */ @@ -355,8 +358,8 @@ public abstract class AAStructureBindingModel { Integer.valueOf(pe).toString() })); } final String nullChain = "TheNullChain"; - List s = new ArrayList(); - List c = new ArrayList(); + List s = new ArrayList<>(); + List c = new ArrayList<>(); if (getChains() == null) { setChains(new String[getPdbCount()][]); @@ -425,8 +428,8 @@ public abstract class AAStructureBindingModel public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe, SequenceI[][] seq, String[][] chns) { - List v = new ArrayList(); - List rtn = new ArrayList(); + List v = new ArrayList<>(); + List rtn = new ArrayList<>(); for (int i = 0; i < getPdbCount(); i++) { v.add(getPdbEntry(i)); @@ -823,4 +826,35 @@ public abstract class AAStructureBindingModel public abstract jalview.api.FeatureRenderer getFeatureRenderer( AlignmentViewPanel alignment); + + /** + * Sets the flag for whether only mapped visible residues in the alignment + * should be visible in the structure viewer + * + * @param b + */ + public void setShowAlignmentOnly(boolean b) + { + showAlignmentOnly = b; + } + + /** + * Answers true if only mapped visible residues in the alignment should be + * visible in the structure viewer, else false + * + * @return + */ + public boolean isShowAlignmentOnly() + { + return showAlignmentOnly; + } + + /** + * Shows the structures in the viewer, without changing their colouring. This is + * to support toggling of whether the whole structure is shown, or only residues + * mapped to visible regions of the alignment. + * + * @param alignViewportI + */ + public abstract void showStructures(AlignViewportI alignViewportI); } -- 1.7.10.2