X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fext%2Frbvi%2Fchimera%2FChimeraCommands.java;h=95757fd3e1a8904131e96a029bba15ba1e0544e4;hb=207b06de28859536277608ad94897eaa526b1279;hp=d34ac255a8622c5a2fab2cb978725e62e287466e;hpb=8c0955545a1a10062467e2d6dc7feb1e855655c0;p=jalview.git diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index d34ac25..95757fd 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -20,11 +20,15 @@ */ package jalview.ext.rbvi.chimera; +import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; +import jalview.datamodel.ColumnSelection; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; @@ -33,6 +37,7 @@ 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; @@ -46,20 +51,26 @@ import java.util.Map; public class ChimeraCommands { + public static final String NAMESPACE_PREFIX = "jv_"; + /** - * utility to construct the commands to colour chains by the given alignment - * for passing to Chimera - * - * @returns Object[] { Object[] { , + * Constructs Chimera commands to colour residues as per the Jalview alignment * + * @param ssm + * @param files + * @param sequence + * @param sr + * @param fr + * @param viewPanel + * @return */ - public static StructureMappingcommandSet getColourBySequenceCommand( + public static StructureMappingcommandSet[] getColourBySequenceCommand( StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, - AlignmentI alignment) + SequenceI[][] sequence, SequenceRenderer sr, + AlignmentViewPanel viewPanel) { - Map colourMap = buildColoursMap( - ssm, files, sequence, sr, fr, alignment); + Map colourMap = buildColoursMap(ssm, files, + sequence, sr, viewPanel); List colourCommands = buildColourCommands(colourMap); @@ -67,7 +78,7 @@ public class ChimeraCommands ChimeraCommands.class, null, colourCommands.toArray(new String[colourCommands.size()])); - return cs; + return new StructureMappingcommandSet[] { cs }; } /** @@ -106,8 +117,7 @@ public class ChimeraCommands } sb.append("color ").append(colourCode).append(" "); firstColour = false; - final AtomSpecModel colourData = colourMap - .get(colour); + final AtomSpecModel colourData = colourMap.get(colour); sb.append(colourData.getAtomSpec()); } commands.add(sb.toString()); @@ -167,9 +177,8 @@ public class ChimeraCommands /** *
-   * Build a data structure which maps contiguous subsequences for each colour. 
-   * This generates a data structure from which we can easily generate the 
-   * Chimera command for colour by sequence.
+   * Build a data structure which records contiguous subsequences for each colour. 
+   * From this we can easily generate the Chimera command for colour by sequence.
    * Color
    *     Model number
    *         Chain
@@ -179,11 +188,17 @@ public class ChimeraCommands
    */
   protected static Map buildColoursMap(
           StructureSelectionManager ssm, String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment)
+          SequenceI[][] sequence, SequenceRenderer sr,
+          AlignmentViewPanel viewPanel)
   {
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+    AlignViewportI viewport = viewPanel.getAlignViewport();
+    ColumnSelection cs = viewport.getColumnSelection();
+    AlignmentI al = viewport.getAlignment();
     Map colourMap = new LinkedHashMap();
     Color lastColour = null;
+
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
@@ -201,9 +216,9 @@ public class ChimeraCommands
         {
           final SequenceI seq = sequence[pdbfnum][s];
           if (mapping[m].getSequence() == seq
-                  && (sp = alignment.findIndex(seq)) > -1)
+                  && (sp = al.findIndex(seq)) > -1)
           {
-            SequenceI asp = alignment.getSequenceAt(sp);
+            SequenceI asp = al.getSequenceAt(sp);
             for (int r = 0; r < asp.getLength(); r++)
             {
               // no mapping to gaps in sequence
@@ -218,7 +233,16 @@ public class ChimeraCommands
                 continue;
               }
 
-              Color colour = sr.getResidueColour(seq, r, fr);
+              Color colour = sr.getResidueColour(seq, r, finder);
+
+              /*
+               * darker colour for hidden regions
+               */
+              if (!cs.isVisible(r))
+              {
+                colour = Color.GRAY;
+              }
+
               final String chain = mapping[m].getChain();
 
               /*
@@ -233,7 +257,7 @@ public class ChimeraCommands
               {
                 if (startPos != -1)
                 {
-                  addRange(colourMap, lastColour, pdbfnum, startPos,
+                  addColourRange(colourMap, lastColour, pdbfnum, startPos,
                           lastPos, lastChain);
                 }
                 startPos = pos;
@@ -245,8 +269,8 @@ public class ChimeraCommands
             // final colour range
             if (lastColour != null)
             {
-              addRange(colourMap, lastColour, pdbfnum, startPos,
-                      lastPos, lastChain);
+              addColourRange(colourMap, lastColour, pdbfnum, startPos, lastPos,
+                      lastChain);
             }
             // break;
           }
@@ -266,7 +290,7 @@ public class ChimeraCommands
    * @param endPos
    * @param chain
    */
-  protected static void addRange(Map map,
+  protected static void addColourRange(Map map,
           Object key, int model, int startPos, int endPos, String chain)
   {
     /*
@@ -283,22 +307,22 @@ public class ChimeraCommands
   }
 
   /**
-   * Constructs and returns a set of Chimera commands to set attributes on
-   * residues corresponding to features in Jalview
+   * Constructs and returns Chimera commands to set attributes on residues
+   * corresponding to features in Jalview. Attribute names are the Jalview
+   * feature type, with a "jv_" prefix.
    * 
    * @param ssm
    * @param files
    * @param seqs
-   * @param fr
-   * @param alignment
+   * @param viewPanel
    * @return
    */
   public static StructureMappingcommandSet getSetAttributeCommandsForFeatures(
           StructureSelectionManager ssm, String[] files,
-          SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment)
+          SequenceI[][] seqs, AlignmentViewPanel viewPanel)
   {
-    Map featureMap = buildFeaturesMap(
-            ssm, files, seqs, fr, alignment);
+    Map> featureMap = buildFeaturesMap(
+            ssm, files, seqs, viewPanel);
 
     List commands = buildSetAttributeCommands(featureMap);
 
@@ -310,27 +334,36 @@ public class ChimeraCommands
   }
 
   /**
-   * Helper method to build a map of { featureType, AtomSpecModel }
+   * 
+   * Helper method to build a map of 
+   *   { featureType, { feature value, AtomSpecModel } }
+   * 
* * @param ssm * @param files * @param seqs - * @param fr - * @param alignment + * @param viewPanel * @return */ - protected static Map buildFeaturesMap( + protected static Map> buildFeaturesMap( StructureSelectionManager ssm, String[] files, - SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment) + SequenceI[][] seqs, AlignmentViewPanel viewPanel) { - Map theMap = new LinkedHashMap(); + Map> theMap = new LinkedHashMap>(); + + FeatureRenderer fr = viewPanel.getFeatureRenderer(); + if (fr == null) + { + return theMap; + } List visibleFeatures = fr.getDisplayedFeatureTypes(); if (visibleFeatures.isEmpty()) { return theMap; } - + + AlignmentI alignment = viewPanel.getAlignment(); for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); @@ -376,7 +409,7 @@ public class ChimeraCommands */ protected static void scanSequenceFeatures(List visibleFeatures, StructureMapping mapping, SequenceI seq, - Map theMap, int modelNumber) + Map> theMap, int modelNumber) { SequenceFeature[] sfs = seq.getSequenceFeatures(); if (sfs == null) @@ -387,7 +420,14 @@ public class ChimeraCommands for (SequenceFeature sf : sfs) { String type = sf.getType(); - if (!visibleFeatures.contains(type)) + + /* + * Only copy visible features, don't copy any which originated + * from Chimera, and suppress uninteresting ones (e.g. RESNUM) + */ + boolean isFromViewer = JalviewChimeraBinding.CHIMERA_FEATURE_GROUP + .equals(sf.getFeatureGroup()); + if (isFromViewer || !visibleFeatures.contains(type)) { continue; } @@ -396,15 +436,25 @@ public class ChimeraCommands if (!mappedRanges.isEmpty()) { - AtomSpecModel atomSpec = theMap.get(type); - if (atomSpec == null) + String value = sf.getDescription(); + if (value == null || value.length() == 0) + { + value = type; + } + float score = sf.getScore(); + if (score != 0f && !Float.isNaN(score)) + { + value = Float.toString(score); + } + Map featureValues = theMap.get(type); + if (featureValues == null) { - atomSpec = new AtomSpecModel(); - theMap.put(type, atomSpec); + featureValues = new HashMap(); + theMap.put(type, featureValues); } for (int[] range : mappedRanges) { - atomSpec.addRange(modelNumber, range[0], range[1], + addColourRange(featureValues, value, modelNumber, range[0], range[1], mapping.getChain()); } } @@ -412,37 +462,87 @@ public class ChimeraCommands } /** - * 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 + * Traverse the map of features/values/models/chains/positions to construct a + * list of 'setattr' commands (one per distinct feature type and value). + *

+ * The format of each command is * *

    * 
setattr r " " #modelnumber:range.chain - * e.g. setattr r jv:chain " " #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,... + * e.g. setattr r jv:chain #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,... *
*
- *

- * Note we are not (currently) setting attribute values, only the type - * (presence) of each attribute. This is to avoid overloading the Chimera REST - * interface by sending too many distinct commands. Analysis by feature values - * may still be performed in Jalview, on selections created in Chimera. * * @param featureMap * @return */ protected static List buildSetAttributeCommands( - Map featureMap) + Map> featureMap) { List commands = new ArrayList(); for (String featureType : featureMap.keySet()) { - StringBuilder sb = new StringBuilder(128); - String sanitised = featureType.replace(" ", "_").replace("-", "_"); - sb.append("setattr r jv_").append(sanitised).append(" \" \" "); - sb.append(featureMap.get(featureType).getAtomSpec()); - commands.add(sb.toString()); + String attributeName = makeAttributeName(featureType); + + /* + * clear down existing attributes for this feature + */ + // 'problem' - sets attribute to None on all residues - overkill? + // commands.add("~setattr r " + attributeName + " :*"); + + Map values = featureMap.get(featureType); + for (Object value : values.keySet()) + { + /* + * for each distinct value recorded for this feature type, + * add a command to set the attribute on the mapped residues + * Put values in single quotes, encoding any embedded single quotes + */ + StringBuilder sb = new StringBuilder(128); + String featureValue = value.toString(); + featureValue = featureValue.replaceAll("\\'", "'"); + sb.append("setattr r ").append(attributeName).append(" '") + .append(featureValue).append("' "); + sb.append(values.get(value).getAtomSpec()); + commands.add(sb.toString()); + } } return commands; } + /** + * Makes a prefixed and valid Chimera attribute name. A jv_ prefix is applied + * for a 'Jalview' namespace, and any non-alphanumeric character is converted + * to an underscore. + * + * @param featureType + * @return

+   * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/setattr.html
+   * 
+ */ + protected static String makeAttributeName(String featureType) + { + StringBuilder sb = new StringBuilder(); + if (featureType != null) + { + for (char c : featureType.toCharArray()) + { + sb.append(Character.isLetterOrDigit(c) ? c : '_'); + } + } + String attName = NAMESPACE_PREFIX + sb.toString(); + + /* + * Chimera treats an attribute name ending in 'color' as colour-valued; + * Jalview doesn't, so prevent this by appending an underscore + */ + if (attName.toUpperCase().endsWith("COLOR")) + { + attName += "_"; + } + + return attName; + } + }