JAL-2295 feature and colour commands refactored to use AtomSpecModel
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 8 Nov 2016 14:55:21 +0000 (14:55 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 8 Nov 2016 14:55:21 +0000 (14:55 +0000)
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java

index 93262aa..d34ac25 100644 (file)
@@ -33,7 +33,6 @@ import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -59,7 +58,7 @@ public class ChimeraCommands
           SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
           AlignmentI alignment)
   {
-    Map<Color, AtomSpecModel> colourMap = buildColoursMap(
+    Map<Object, AtomSpecModel> colourMap = buildColoursMap(
             ssm, files, sequence, sr, fr, alignment);
 
     List<String> colourCommands = buildColourCommands(colourMap);
@@ -87,7 +86,7 @@ public class ChimeraCommands
    * @return
    */
   protected static List<String> buildColourCommands(
-          Map<Color, AtomSpecModel> colourMap)
+          Map<Object, AtomSpecModel> colourMap)
   {
     /*
      * This version concatenates all commands into a single String (semi-colon
@@ -97,8 +96,9 @@ public class ChimeraCommands
     List<String> commands = new ArrayList<String>();
     StringBuilder sb = new StringBuilder(256);
     boolean firstColour = true;
-    for (Color colour : colourMap.keySet())
+    for (Object key : colourMap.keySet())
     {
+      Color colour = (Color) key;
       String colourCode = ColorUtils.toTkCode(colour);
       if (!firstColour)
       {
@@ -177,12 +177,12 @@ public class ChimeraCommands
    * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
    * </pre>
    */
-  protected static Map<Color, AtomSpecModel> buildColoursMap(
+  protected static Map<Object, AtomSpecModel> buildColoursMap(
           StructureSelectionManager ssm, String[] files,
           SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
           AlignmentI alignment)
   {
-    Map<Color, AtomSpecModel> colourMap = new LinkedHashMap<Color, AtomSpecModel>();
+    Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<Object, AtomSpecModel>();
     Color lastColour = null;
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
@@ -233,7 +233,7 @@ public class ChimeraCommands
               {
                 if (startPos != -1)
                 {
-                  addColourRange(colourMap, lastColour, pdbfnum, startPos,
+                  addRange(colourMap, lastColour, pdbfnum, startPos,
                           lastPos, lastChain);
                 }
                 startPos = pos;
@@ -245,7 +245,7 @@ public class ChimeraCommands
             // final colour range
             if (lastColour != null)
             {
-              addColourRange(colourMap, lastColour, pdbfnum, startPos,
+              addRange(colourMap, lastColour, pdbfnum, startPos,
                       lastPos, lastChain);
             }
             // break;
@@ -259,34 +259,32 @@ public class ChimeraCommands
   /**
    * Helper method to add one contiguous colour range to the colour map.
    * 
-   * @param colourMap
-   * @param colour
+   * @param map
+   * @param key
    * @param model
    * @param startPos
    * @param endPos
    * @param chain
    */
-  protected static void addColourRange(
-Map<Color, AtomSpecModel> colourMap,
-          Color colour, int model, int startPos, int endPos, String chain)
+  protected static void addRange(Map<Object, AtomSpecModel> map,
+          Object key, int model, int startPos, int endPos, String chain)
   {
-    // refactor for reuse as addRange
     /*
      * Get/initialize map of data for the colour
      */
-    AtomSpecModel colourData = colourMap.get(colour);
-    if (colourData == null)
+    AtomSpecModel atomSpec = map.get(key);
+    if (atomSpec == null)
     {
-      colourData = new AtomSpecModel();
-      colourMap.put(colour, colourData);
+      atomSpec = new AtomSpecModel();
+      map.put(key, atomSpec);
     }
 
-    colourData.addRange(model, startPos, endPos, chain);
+    atomSpec.addRange(model, startPos, endPos, chain);
   }
 
   /**
    * Constructs and returns a set of Chimera commands to set attributes on
-   * residues corresponding to features in Jalview.
+   * residues corresponding to features in Jalview
    * 
    * @param ssm
    * @param files
@@ -299,23 +297,20 @@ Map<Color, AtomSpecModel> colourMap,
           StructureSelectionManager ssm, String[] files,
           SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment)
   {
-    Map<String, Map<Integer, Map<String, List<int[]>>>> featureMap = buildFeaturesMap(
+    Map<String, AtomSpecModel> featureMap = buildFeaturesMap(
             ssm, files, seqs, fr, alignment);
 
-    List<String> colourCommands = buildSetAttributeCommands(featureMap);
+    List<String> commands = buildSetAttributeCommands(featureMap);
 
     StructureMappingcommandSet cs = new StructureMappingcommandSet(
             ChimeraCommands.class, null,
-            colourCommands.toArray(new String[colourCommands.size()]));
+            commands.toArray(new String[commands.size()]));
 
     return cs;
   }
 
   /**
-   * <pre>
-   * Helper method to build a map of 
-   * { featureType, {modelNumber, {chain, {list of from-to ranges} } } }
-   * </pre>
+   * Helper method to build a map of { featureType, AtomSpecModel }
    * 
    * @param ssm
    * @param files
@@ -324,11 +319,11 @@ Map<Color, AtomSpecModel> colourMap,
    * @param alignment
    * @return
    */
-  protected static Map<String, Map<Integer, Map<String, List<int[]>>>> buildFeaturesMap(
+  protected static Map<String, AtomSpecModel> buildFeaturesMap(
           StructureSelectionManager ssm, String[] files,
           SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment)
   {
-    Map<String, Map<Integer, Map<String, List<int[]>>>> theMap = new HashMap<String, Map<Integer, Map<String, List<int[]>>>>();
+    Map<String, AtomSpecModel> theMap = new LinkedHashMap<String, AtomSpecModel>();
 
     List<String> visibleFeatures = fr.getDisplayedFeatureTypes();
     if (visibleFeatures.isEmpty())
@@ -336,9 +331,6 @@ Map<Color, AtomSpecModel> colourMap,
       return theMap;
     }
     
-    /*
-     * traverse mappings to structures 
-     */
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
@@ -348,7 +340,6 @@ Map<Color, AtomSpecModel> colourMap,
         continue;
       }
 
-      int lastPos = -1;
       for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++)
       {
         for (int m = 0; m < mapping.length; m++)
@@ -357,49 +348,70 @@ Map<Color, AtomSpecModel> colourMap,
           int sp = alignment.findIndex(seq);
           if (mapping[m].getSequence() == seq && sp > -1)
           {
-            SequenceI asp = alignment.getSequenceAt(sp);
-
             /*
-             * traverse each sequence for its mapped positions
+             * found a sequence with a mapping to a structure;
+             * now scan its features
              */
-            for (int r = 0; r < asp.getLength(); r++)
-            {
-              // no mapping to gaps in sequence
-              if (Comparison.isGap(asp.getCharAt(r)))
-              {
-                continue;
-              }
-              int residuePos = asp.findPosition(r);
-              int pos = mapping[m].getPDBResNum(residuePos);
-
-              if (pos < 1 || pos == lastPos)
-              {
-                continue;
-              }
-              final String chain = mapping[m].getChain();
+            SequenceI asp = alignment.getSequenceAt(sp);
 
-              /*
-               * record any features at this position, with the model, chain
-               * and residue number they map to
-               */
-              List<SequenceFeature> features = fr.findFeaturesAtRes(asp,
-                      residuePos);
-              for (SequenceFeature feature : features)
-              {
-                if (!visibleFeatures.contains(feature))
-                {
-                  continue;
-                }
-              }
-            }
+            scanSequenceFeatures(visibleFeatures, mapping[m], asp, theMap,
+                    pdbfnum);
           }
         }
       }
-      }
+    }
     return theMap;
   }
 
   /**
+   * Inspect features on the sequence; for each feature that is visible,
+   * determine its mapped ranges in the structure (if any) according to the
+   * given mapping, and add them to the map
+   * 
+   * @param visibleFeatures
+   * @param mapping
+   * @param seq
+   * @param theMap
+   * @param modelNumber
+   */
+  protected static void scanSequenceFeatures(List<String> visibleFeatures,
+          StructureMapping mapping, SequenceI seq,
+          Map<String, AtomSpecModel> theMap, int modelNumber)
+  {
+    SequenceFeature[] sfs = seq.getSequenceFeatures();
+    if (sfs == null)
+    {
+      return;
+    }
+
+    for (SequenceFeature sf : sfs)
+    {
+      String type = sf.getType();
+      if (!visibleFeatures.contains(type))
+      {
+        continue;
+      }
+      List<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
+              sf.getEnd());
+
+      if (!mappedRanges.isEmpty())
+      {
+        AtomSpecModel atomSpec = theMap.get(type);
+        if (atomSpec == null)
+        {
+          atomSpec = new AtomSpecModel();
+          theMap.put(type, atomSpec);
+        }
+        for (int[] range : mappedRanges)
+        {
+          atomSpec.addRange(modelNumber, range[0], range[1],
+                  mapping.getChain());
+        }
+      }
+    }
+  }
+
+  /**
    * Traverse the map of features/models/chains/positions to construct a list of
    * 'setattr' commands (one per feature type). The format of each command is
    * 
@@ -416,22 +428,17 @@ Map<Color, AtomSpecModel> colourMap,
    * 
    * @param featureMap
    * @return
-   * @see http 
-   *      ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec
-   *      .html
    */
   protected static List<String> buildSetAttributeCommands(
-          Map<String, Map<Integer, Map<String, List<int[]>>>> featureMap)
+          Map<String, AtomSpecModel> featureMap)
   {
     List<String> commands = new ArrayList<String>();
     for (String featureType : featureMap.keySet())
     {
       StringBuilder sb = new StringBuilder(128);
-      featureType = featureType.replace(" ", "_");
-      sb.append("setattr r jv:").append(featureType).append(" \" \" ");
-      final Map<Integer, Map<String, List<int[]>>> featureData = featureMap
-              .get(featureType);
-      sb.append(getAtomSpec(featureData));
+      String sanitised = featureType.replace(" ", "_").replace("-", "_");
+      sb.append("setattr r jv_").append(sanitised).append(" \" \" ");
+      sb.append(featureMap.get(featureType).getAtomSpec());
       commands.add(sb.toString());
     }
 
index 3c6f830..b534b4f 100644 (file)
@@ -34,16 +34,16 @@ public class ChimeraCommandsTest
   public void testBuildColourCommands()
   {
 
-    Map<Color, AtomSpecModel> map = new LinkedHashMap<Color, AtomSpecModel>();
-    ChimeraCommands.addColourRange(map, Color.blue, 0, 2, 5, "A");
-    ChimeraCommands.addColourRange(map, Color.blue, 0, 7, 7, "B");
-    ChimeraCommands.addColourRange(map, Color.blue, 0, 9, 23, "A");
-    ChimeraCommands.addColourRange(map, Color.blue, 1, 1, 1, "A");
-    ChimeraCommands.addColourRange(map, Color.blue, 1, 4, 7, "B");
-    ChimeraCommands.addColourRange(map, Color.yellow, 1, 8, 8, "A");
-    ChimeraCommands.addColourRange(map, Color.yellow, 1, 3, 5, "A");
-    ChimeraCommands.addColourRange(map, Color.red, 0, 3, 5, "A");
-    ChimeraCommands.addColourRange(map, Color.red, 0, 6, 9, "A");
+    Map<Object, AtomSpecModel> map = new LinkedHashMap<Object, AtomSpecModel>();
+    ChimeraCommands.addRange(map, Color.blue, 0, 2, 5, "A");
+    ChimeraCommands.addRange(map, Color.blue, 0, 7, 7, "B");
+    ChimeraCommands.addRange(map, Color.blue, 0, 9, 23, "A");
+    ChimeraCommands.addRange(map, Color.blue, 1, 1, 1, "A");
+    ChimeraCommands.addRange(map, Color.blue, 1, 4, 7, "B");
+    ChimeraCommands.addRange(map, Color.yellow, 1, 8, 8, "A");
+    ChimeraCommands.addRange(map, Color.yellow, 1, 3, 5, "A");
+    ChimeraCommands.addRange(map, Color.red, 0, 3, 5, "A");
+    ChimeraCommands.addRange(map, Color.red, 0, 6, 9, "A");
 
     // Colours should appear in the Chimera command in the order in which
     // they were added; within colour, by model, by chain, ranges in start order