JAL-3390 first draft of showing only visible alignment in Chimera
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 2 Aug 2019 15:51:22 +0000 (16:51 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 2 Aug 2019 15:51:22 +0000 (16:51 +0100)
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/StructureViewerBase.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/structures/models/AAStructureBindingModel.java

index c0a1e0d..762b08e 100644 (file)
@@ -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
+  }
 }
index dad8511..ec94e7d 100644 (file)
@@ -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<String> commands = new ArrayList<String>();
+    List<String> 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<Object, AtomSpecModel> colourMap = new LinkedHashMap<Object, AtomSpecModel>();
+    Map<Object, AtomSpecModel> 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<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
+    Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<>();
 
     FeatureRenderer fr = viewPanel.getFeatureRenderer();
     if (fr == null)
@@ -445,7 +445,7 @@ public class ChimeraCommands
         Map<Object, AtomSpecModel> featureValues = theMap.get(type);
         if (featureValues == null)
         {
-          featureValues = new HashMap<Object, AtomSpecModel>();
+          featureValues = new HashMap<>();
           theMap.put(type, featureValues);
         }
         for (int[] range : mappedRanges)
@@ -475,7 +475,7 @@ public class ChimeraCommands
   protected static List<String> buildSetAttributeCommands(
           Map<String, Map<Object, AtomSpecModel>> featureMap)
   {
-    List<String> commands = new ArrayList<String>();
+    List<String> commands = new ArrayList<>();
     for (String featureType : featureMap.keySet())
     {
       String attributeName = makeAttributeName(featureType);
index 00446f2..6fa06d2 100644 (file)
@@ -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<String> chainNames = new ArrayList<String>();
+  private List<String> chainNames = new ArrayList<>();
 
-  private Hashtable<String, String> chainFile = new Hashtable<String, String>();
+  private Hashtable<String, String> 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<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<String, List<ChimeraModel>>();
+  private Map<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<>();
 
   String lastHighlightCommand;
 
@@ -133,7 +136,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     String file = pe.getFile();
     try
     {
-      List<ChimeraModel> modelsToMap = new ArrayList<ChimeraModel>();
+      List<ChimeraModel> modelsToMap = new ArrayList<>();
       List<ChimeraModel> 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<String> 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<AtomSpec> convertStructureResiduesToAlignment(
           List<String> structureSelection)
   {
-    List<AtomSpec> atomSpecs = new ArrayList<AtomSpec>();
+    List<AtomSpec> 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)
+   * 
+   * <pre>
+   * #0:2-94.A | #1:1-93.C | #2:1-93.A
+   * </pre>
+   * 
+   * 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<int[]> 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();
+  }
 }
index a7349b8..6c35a9c 100644 (file)
@@ -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;
   }
 
index 35a5475..5c50044 100644 (file)
@@ -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
index 83d8590..2094201 100644 (file)
@@ -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;
index 2528286..21b0e3d 100644 (file)
@@ -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<SequenceI> s = new ArrayList<SequenceI>();
-    List<String> c = new ArrayList<String>();
+    List<SequenceI> s = new ArrayList<>();
+    List<String> 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<PDBEntry> v = new ArrayList<PDBEntry>();
-    List<int[]> rtn = new ArrayList<int[]>();
+    List<PDBEntry> v = new ArrayList<>();
+    List<int[]> 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);
 }