JAL-1609 wip on Chimera colour by sequence
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 3 Dec 2014 15:00:53 +0000 (15:00 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 3 Dec 2014 15:00:53 +0000 (15:00 +0000)
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/schemes/Blosum62ColourScheme.java
src/jalview/ws/HttpClientUtils.java

index f65f00b..a76c7e0 100644 (file)
@@ -746,7 +746,8 @@ public class ChimeraManager
    */
   public List<String> sendChimeraCommand(String command, boolean reply)
   {
-    if (!isChimeraLaunched())
+    if (!isChimeraLaunched() || command == null
+            || "".equals(command.trim()))
     {
       return null;
     }
index 4ee74aa..3c47ed1 100644 (file)
@@ -27,10 +27,15 @@ import jalview.datamodel.SequenceI;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.Comparison;
 
 import java.awt.Color;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -54,15 +59,31 @@ public class ChimeraCommands
           SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
           AlignmentI alignment)
   {
-
-    ArrayList<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
+    String defAttrPath = null;
+    FileOutputStream fos = null;
+    try
+    {
+      File outFile = File.createTempFile("jalviewdefattr", ".xml");
+      outFile.deleteOnExit();
+      defAttrPath = outFile.getPath();
+      fos = new FileOutputStream(outFile);
+      fos.write("attribute: jalviewclr\n".getBytes());
+    } catch (IOException e1)
+    {
+      e1.printStackTrace();
+    }
+    List<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
 
     /*
      * Map of { colour, positionSpecs}
      */
     Map<String, StringBuilder> colranges = new LinkedHashMap<String, StringBuilder>();
+    StringBuilder setAttributes = new StringBuilder(256);
+    String lastColour = "none";
+    Color lastCol = null;
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
+      boolean startModel = true;
       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
 
       if (mapping == null || mapping.length < 1)
@@ -70,21 +91,21 @@ public class ChimeraCommands
         continue;
       }
 
-      int startPos = -1, lastPos = -1, startModel = -1, lastModel = -1;
+      int startPos = -1, lastPos = -1;
       String lastChain = "";
-      Color lastCol = null;
       for (int s = 0; s < sequence[pdbfnum].length; s++)
       {
         for (int sp, m = 0; m < mapping.length; m++)
         {
-          if (mapping[m].getSequence() == sequence[pdbfnum][s]
-                  && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
+          final SequenceI seq = sequence[pdbfnum][s];
+          if (mapping[m].getSequence() == seq
+                  && (sp = alignment.findIndex(seq)) > -1)
           {
             SequenceI asp = alignment.getSequenceAt(sp);
             for (int r = 0; r < asp.getLength(); r++)
             {
               // no mapping to gaps in sequence
-              if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
+              if (Comparison.isGap(asp.getCharAt(r)))
               {
                 continue;
               }
@@ -95,51 +116,152 @@ public class ChimeraCommands
                 continue;
               }
 
-              Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r);
-
-              if (fr != null)
+              Color col = getResidueColour(seq, r, sr, fr);
+              /*
+               * Just keep incrementing the end position for this colour range
+               * _unless_ colour, PDB model or chain has changed, or there is a
+               * gap in the mapped residue sequence
+               */
+              final boolean newColour = !col.equals(lastCol);
+              final boolean nonContig = lastPos + 1 != pos;
+              final boolean newChain = !mapping[m].getChain().equals(lastChain);
+              if (newColour || nonContig || startModel || newChain)
               {
-                col = fr.findFeatureColour(col, sequence[pdbfnum][s], r);
-              }
-              if (lastCol != col || lastPos + 1 != pos
-                      || pdbfnum != lastModel
-                      || !mapping[m].getChain().equals(lastChain))
-              {
-                if (lastCol != null)
+                if (/* lastCol != null */startPos != -1)
                 {
-                  addColourRange(colranges, lastCol,startModel,startPos,lastPos,lastChain); 
+                  addColourRange(colranges, lastCol, pdbfnum, startPos,
+                          lastPos, lastChain, startModel);
+                  startModel = false;
                 }
-                lastCol = null;
+                // lastCol = null;
                 startPos = pos;
-                startModel = pdbfnum;
               }
               lastCol = col;
               lastPos = pos;
-              lastModel = pdbfnum;
+              // lastModel = pdbfnum;
               lastChain = mapping[m].getChain();
             }
             // final colour range
             if (lastCol != null)
             {
-              addColourRange(colranges, lastCol,startModel,startPos,lastPos,lastChain); 
+              addColourRange(colranges, lastCol, pdbfnum, startPos,
+                      lastPos, lastChain, false);
             }
             break;
           }
         }
       }
-      // Finally, add the command set ready to be returned.
-      StringBuilder coms = new StringBuilder(256);
-      for (String cr:colranges.keySet())
+    }
+      try
+      {
+      lastColour = buildColourCommands(cset, colranges,
+                fos, setAttributes);
+      } catch (IOException e)
       {
-        coms.append("color #"+cr+" "+colranges.get(cr)+";");
+        e.printStackTrace();
       }
-      cset.add(new StructureMappingcommandSet(ChimeraCommands.class,
-              files[pdbfnum], new String[] { coms.toString() }));
+
+    try
+    {
+      fos.close();
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+
+    /*
+     * Send a rangeColor command, preceded by either defattr or setattr,
+     * whichever we end up preferring!
+     * 
+     * rangecolor requires a minimum of two attribute values to operate on
+     */
+    StringBuilder rangeColor = new StringBuilder(256);
+    rangeColor.append("rangecolor jalviewclr");
+    int colourId = 0;
+    for (String colour : colranges.keySet())
+    {
+      colourId++;
+      rangeColor.append(" " + colourId + " " + colour);
     }
+    String rangeColorCommand = rangeColor.toString();
+    if (rangeColorCommand.split(" ").length < 5)
+    {
+      rangeColorCommand += " max " + lastColour;
+    }
+    final String defAttrCommand = "defattr " + defAttrPath
+            + " raiseTool false";
+    final String setAttrCommand = setAttributes.toString();
+    final String attrCommand = false ? defAttrCommand : setAttrCommand;
+    cset.add(new StructureMappingcommandSet(ChimeraCommands.class, null,
+            new String[]
+            { attrCommand /* , rangeColorCommand */}));
+
     return cset.toArray(new StructureMappingcommandSet[cset.size()]);
   }
 
   /**
+   * Get the residue colour at the given sequence position - as determined by
+   * the sequence group colour (if any), else the colour scheme, possibly
+   * overridden by a feature colour.
+   * 
+   * @param seq
+   * @param position
+   * @param sr
+   * @param fr
+   * @return
+   */
+  protected static Color getResidueColour(final SequenceI seq,
+          int position, SequenceRenderer sr, FeatureRenderer fr)
+  {
+    Color col = sr.getResidueBoxColour(seq, position);
+
+    if (fr != null)
+    {
+      col = fr.findFeatureColour(col, seq, position);
+    }
+    return col;
+  }
+
+  /**
+   * Helper method to build the colour commands for one PDBfile.
+   * 
+   * @param cset
+   *          the list of commands to be added to
+   * @param colranges
+   *          the map of colours to residue positions already determined
+   * @param fos
+   *          file to write 'defattr' commands to
+   * @param setAttributes
+   * @throws IOException
+   */
+  protected static String buildColourCommands(
+          List<StructureMappingcommandSet> cset,
+          Map<String, StringBuilder> colranges,
+          FileOutputStream fos, StringBuilder setAttributes)
+          throws IOException
+  {
+    int colourId = 0;
+    String lastColour = null;
+    for (String colour : colranges.keySet())
+    {
+      lastColour = colour;
+      colourId++;
+      /*
+       * Using color command directly is slow for larger structures.
+       * setAttributes.append("color #" + colour + " " + colranges.get(colour)+
+       * ";");
+       */
+      setAttributes.append("color " + colour + " " + colranges.get(colour)
+              + ";");
+      final String atomSpec = new String(colranges.get(colour));
+      // setAttributes.append("setattr r jalviewclr " + colourId + " "
+      // + atomSpec + ";");
+      fos.write(("\t" + atomSpec + "\t" + colourId + "\n").getBytes());
+    }
+    return lastColour;
+  }
+
+  /**
    * Helper method to record a range of positions of the same colour.
    * 
    * @param colranges
@@ -148,12 +270,14 @@ public class ChimeraCommands
    * @param startPos
    * @param endPos
    * @param chain
+   * @param changeModel
    */
   private static void addColourRange(Map<String, StringBuilder> colranges,
-          Color colour, int model,
-          int startPos, int endPos, String chain)
+          Color colour, int model, int startPos, int endPos, String chain,
+          boolean startModel)
   {
-    String colstring = ((colour.getRed()< 16) ? "0":"")+Integer.toHexString(colour.getRed())
+    String colstring = "#" + ((colour.getRed() < 16) ? "0" : "")
+            + Integer.toHexString(colour.getRed())
             + ((colour.getGreen()< 16) ? "0":"")+Integer.toHexString(colour.getGreen())
             + ((colour.getBlue()< 16) ? "0":"")+Integer.toHexString(colour.getBlue());
     StringBuilder currange = colranges.get(colstring);
@@ -161,12 +285,31 @@ public class ChimeraCommands
     {
       colranges.put(colstring, currange = new StringBuilder(256));
     }
-    if (currange.length() > 0)
+    /*
+     * Format as (e.g.) #0:1-3.A,5.A,7-10.A,...#1:1-4.B,..etc
+     */
+    // if (currange.length() > 0)
+    // {
+    // currange.append("|");
+    // }
+    // currange.append("#" + model + ":" + ((startPos==endPos) ? startPos :
+    // startPos + "-"
+    // + endPos) + "." + chain);
+    if (currange.length() == 0)
+    {
+      currange.append("#" + model + ":");
+    }
+    else if (startModel)
+    {
+      currange.append(",#" + model + ":");
+    }
+    else
     {
-      currange.append("|");
+      currange.append(",");
     }
-    currange.append("#" + model + ":" + ((startPos==endPos) ? startPos : startPos + "-"
-            + endPos) + "." + chain);
+    final String rangeSpec = (startPos == endPos) ? Integer
+            .toString(startPos) : (startPos + "-" + endPos);
+    currange.append(rangeSpec + "." + chain);
   }
 
 }
index 41f7781..70c7685 100755 (executable)
 package jalview.schemes;
 
 import jalview.analysis.AAFrequency;
-
-import java.awt.Color;
-import java.util.Map;
-
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 
+import java.awt.Color;
+import java.util.Map;
+
 public class Blosum62ColourScheme extends ResidueColourScheme
 {
   public Blosum62ColourScheme()
@@ -59,6 +58,7 @@ public class Blosum62ColourScheme extends ResidueColourScheme
 
       if (max.indexOf(res) > -1)
       {
+        // TODO use a constant here?
         currentColour = new Color(154, 154, 255);
       }
       else
@@ -74,6 +74,7 @@ public class Blosum62ColourScheme extends ResidueColourScheme
 
         if (c > 0)
         {
+          // TODO use a constant here?
           currentColour = new Color(204, 204, 255);
         }
         else
index 229fa4e..1bdf64f 100644 (file)
@@ -29,6 +29,7 @@ import java.util.List;
 
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
 import org.apache.http.NameValuePair;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpClient;
@@ -40,6 +41,9 @@ import org.apache.http.entity.mime.content.FileBody;
 import org.apache.http.entity.mime.content.InputStreamBody;
 import org.apache.http.entity.mime.content.StringBody;
 import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
 
 /**
  * Helpful procedures for working with services via HTTPClient
@@ -64,7 +68,10 @@ public class HttpClientUtils
           List<NameValuePair> vals) throws ClientProtocolException,
           IOException
   {
-    HttpClient httpclient = new DefaultHttpClient();
+    HttpParams params = new BasicHttpParams();
+    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
+            HttpVersion.HTTP_1_1);
+    HttpClient httpclient = new DefaultHttpClient(params);
     HttpPost httppost = new HttpPost(postUrl);
     UrlEncodedFormEntity ue = new UrlEncodedFormEntity(vals, "UTF-8");
     httppost.setEntity(ue);