Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.ext.rbvi.chimera

File ChimeraCommands.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
12% of files have more coverage

Code metrics

14
96
26
1
473
260
33
0.34
3.69
26
1.27

Classes

Class Line # Actions
ChimeraCommands 41 96 33
0.87587.5%
 

Contributing tests

This file is covered by 30 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.ext.rbvi.chimera;
22   
23    import java.awt.Color;
24    import java.util.ArrayList;
25    import java.util.Arrays;
26    import java.util.List;
27    import java.util.Map;
28   
29    import jalview.structure.AtomSpecModel;
30    import jalview.structure.StructureCommand;
31    import jalview.structure.StructureCommandI;
32    import jalview.structure.StructureCommandsBase;
33    import jalview.util.ColorUtils;
34   
35    /**
36    * Routines for generating Chimera commands for Jalview/Chimera binding
37    *
38    * @author JimP
39    *
40    */
 
41    public class ChimeraCommands extends StructureCommandsBase
42    {
43    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/focus.html
44    private static final StructureCommand FOCUS_VIEW = new StructureCommand("focus");
45   
46    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/listen.html#listresattr
47    private static final StructureCommand LIST_RESIDUE_ATTRIBUTES = new StructureCommand("list resattr");
48   
49    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/stop.html
50    private static final StructureCommand CLOSE_CHIMERA = new StructureCommand("stop really");
51   
52    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/listen.html
53    private static final StructureCommand STOP_NOTIFY_SELECTION = new StructureCommand("listen stop selection");
54   
55    private static final StructureCommand STOP_NOTIFY_MODELS = new StructureCommand("listen stop models");
56   
57    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/listen.html#listselection
58    private static final StructureCommand GET_SELECTION = new StructureCommand("list selection level residue");
59   
60    private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
61    "~display all;~ribbon;chain @CA|P");
62   
63    private static final StructureCommandI COLOUR_BY_CHARGE = new StructureCommand(
64    "color white;color red ::ASP,GLU;color blue ::LYS,ARG;color yellow ::CYS");
65   
66    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/rainbow.html
67    private static final StructureCommandI COLOUR_BY_CHAIN = new StructureCommand(
68    "rainbow chain");
69   
70    // Chimera clause to exclude alternate locations in atom selection
71    private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
72   
 
73  4 toggle @Override
74    public StructureCommandI colourResidues(String atomSpec, Color colour)
75    {
76    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/color.html
77  4 String colourCode = getColourString(colour);
78  4 return new StructureCommand("color " + colourCode + " " + atomSpec);
79    }
80   
81    /**
82    * Returns a colour formatted suitable for use in viewer command syntax
83    *
84    * @param colour
85    * @return
86    */
 
87  8 toggle protected String getColourString(Color colour)
88    {
89  8 return ColorUtils.toTkCode(colour);
90    }
91   
92    /**
93    * Traverse the map of features/values/models/chains/positions to construct a
94    * list of 'setattr' commands (one per distinct feature type and value).
95    * <p>
96    * The format of each command is
97    *
98    * <pre>
99    * <blockquote> setattr r <featureName> " " #modelnumber:range.chain
100    * e.g. setattr r jv_chain &lt;value&gt; #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
101    * </blockquote>
102    * </pre>
103    *
104    * @param featureMap
105    * @return
106    */
 
107  10 toggle @Override
108    public List<StructureCommandI> setAttributes(
109    Map<String, Map<Object, AtomSpecModel>> featureMap)
110    {
111  10 List<StructureCommandI> commands = new ArrayList<>();
112  10 for (String featureType : featureMap.keySet())
113    {
114  10 String attributeName = makeAttributeName(featureType);
115   
116    /*
117    * clear down existing attributes for this feature
118    */
119    // 'problem' - sets attribute to None on all residues - overkill?
120    // commands.add("~setattr r " + attributeName + " :*");
121   
122  10 Map<Object, AtomSpecModel> values = featureMap.get(featureType);
123  10 for (Object value : values.keySet())
124    {
125    /*
126    * for each distinct value recorded for this feature type,
127    * add a command to set the attribute on the mapped residues
128    * Put values in single quotes, encoding any embedded single quotes
129    */
130  12 AtomSpecModel atomSpecModel = values.get(value);
131  12 String featureValue = value.toString();
132  12 featureValue = featureValue.replaceAll("\\'", "&#39;");
133  12 StructureCommandI cmd = setAttribute(attributeName, featureValue,
134    atomSpecModel);
135  12 commands.add(cmd);
136    }
137    }
138   
139  10 return commands;
140    }
141   
142    /**
143    * Returns a viewer command to set the given residue attribute value on
144    * residues specified by the AtomSpecModel, for example
145    *
146    * <pre>
147    * setatr res jv_chain 'primary' #1:12-34,48-55.B
148    * </pre>
149    *
150    * @param attributeName
151    * @param attributeValue
152    * @param atomSpecModel
153    * @return
154    */
 
155  7 toggle protected StructureCommandI setAttribute(String attributeName,
156    String attributeValue,
157    AtomSpecModel atomSpecModel)
158    {
159  7 StringBuilder sb = new StringBuilder(128);
160  7 sb.append("setattr res ").append(attributeName).append(" '")
161    .append(attributeValue).append("' ");
162  7 sb.append(getAtomSpec(atomSpecModel, false));
163  7 return new StructureCommand(sb.toString());
164    }
165   
166    /**
167    * Makes a prefixed and valid Chimera attribute name. A jv_ prefix is applied
168    * for a 'Jalview' namespace, and any non-alphanumeric character is converted
169    * to an underscore.
170    *
171    * @param featureType
172    * @return
173    * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/setattr.html
174    */
 
175  16 toggle @Override
176    protected String makeAttributeName(String featureType)
177    {
178  16 String attName = super.makeAttributeName(featureType);
179   
180    /*
181    * Chimera treats an attribute name ending in 'color' as colour-valued;
182    * Jalview doesn't, so prevent this by appending an underscore
183    */
184  16 if (attName.toUpperCase().endsWith("COLOR"))
185    {
186  1 attName += "_";
187    }
188   
189  16 return attName;
190    }
191   
 
192  2 toggle @Override
193    public StructureCommandI colourByChain()
194    {
195  2 return COLOUR_BY_CHAIN;
196    }
197   
 
198  1 toggle @Override
199    public List<StructureCommandI> colourByCharge()
200    {
201  1 return Arrays.asList(COLOUR_BY_CHARGE);
202    }
203   
 
204  1 toggle @Override
205    public String getResidueSpec(String residue)
206    {
207  1 return "::" + residue;
208    }
209   
 
210  2 toggle @Override
211    public StructureCommandI setBackgroundColour(Color col)
212    {
213    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/set.html#bgcolor
214  2 return new StructureCommand("set bgColor " + ColorUtils.toTkCode(col));
215    }
216   
 
217  1 toggle @Override
218    public StructureCommandI focusView()
219    {
220  1 return FOCUS_VIEW;
221    }
222   
 
223  0 toggle @Override
224    public List<StructureCommandI> showChains(List<String> toShow)
225    {
226    /*
227    * Construct a chimera command like
228    *
229    * ~display #*;~ribbon #*;ribbon :.A,:.B
230    */
231  0 StringBuilder cmd = new StringBuilder(64);
232  0 boolean first = true;
233  0 for (String chain : toShow)
234    {
235  0 String[] tokens = chain.split(":");
236  0 if (tokens.length == 2)
237    {
238  0 String showChainCmd = tokens[0] + ":." + tokens[1];
239  0 if (!first)
240    {
241  0 cmd.append(",");
242    }
243  0 cmd.append(showChainCmd);
244  0 first = false;
245    }
246    }
247   
248    /*
249    * could append ";focus" to this command to resize the display to fill the
250    * window, but it looks more helpful not to (easier to relate chains to the
251    * whole)
252    */
253  0 final String command = "~display #*; ~ribbon #*; ribbon :"
254    + cmd.toString();
255  0 return Arrays.asList(new StructureCommand(command));
256    }
257   
 
258  1 toggle @Override
259    public List<StructureCommandI> superposeStructures(AtomSpecModel ref,
260    AtomSpecModel spec)
261    {
262    /*
263    * Form Chimera match command to match spec to ref
264    * (the first set of atoms are moved on to the second)
265    *
266    * match #1:1-30.B,81-100.B@CA #0:21-40.A,61-90.A@CA
267    *
268    * @see https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/midas/match.html
269    */
270  1 StringBuilder cmd = new StringBuilder();
271  1 String atomSpecAlphaOnly = getAtomSpec(spec, true);
272  1 String refSpecAlphaOnly = getAtomSpec(ref, true);
273  1 cmd.append("match ").append(atomSpecAlphaOnly).append(" ").append(refSpecAlphaOnly);
274   
275    /*
276    * show superposed residues as ribbon
277    */
278  1 String atomSpec = getAtomSpec(spec, false);
279  1 String refSpec = getAtomSpec(ref, false);
280  1 cmd.append("; ribbon ");
281  1 cmd.append(atomSpec).append("|").append(refSpec).append("; focus");
282   
283  1 return Arrays.asList(new StructureCommand(cmd.toString()));
284    }
285   
 
286  1 toggle @Override
287    public StructureCommandI openCommandFile(String path)
288    {
289    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/filetypes.html
290  1 return new StructureCommand("open cmd:" + path);
291    }
292   
 
293  1 toggle @Override
294    public StructureCommandI saveSession(String filepath)
295    {
296    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/save.html
297  1 return new StructureCommand("save " + filepath);
298    }
299   
300    /**
301    * Returns the range(s) modelled by {@code atomSpec} formatted as a Chimera
302    * atomspec string, e.g.
303    *
304    * <pre>
305    * #0:15.A,28.A,54.A,70-72.A|#1:2.A,6.A,11.A,13-14.A
306    * </pre>
307    *
308    * where
309    * <ul>
310    * <li>#0 is a model number</li>
311    * <li>15 or 70-72 is a residue number, or range of residue numbers</li>
312    * <li>.A is a chain identifier</li>
313    * <li>residue ranges are separated by comma</li>
314    * <li>atomspecs for distinct models are separated by | (or)</li>
315    * </ul>
316    *
317    * <pre>
318    *
319    * @param model
320    * @param alphaOnly
321    * @return
322    * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
323    */
 
324  41 toggle @Override
325    public String getAtomSpec(AtomSpecModel atomSpec, boolean alphaOnly)
326    {
327  41 StringBuilder sb = new StringBuilder(128);
328  41 boolean firstModel = true;
329  41 for (String model : atomSpec.getModels())
330    {
331  60 if (!firstModel)
332    {
333  21 sb.append("|");
334    }
335  60 firstModel = false;
336  60 appendModel(sb, model, atomSpec, alphaOnly);
337    }
338  41 return sb.toString();
339    }
340   
341    /**
342    * A helper method to append an atomSpec string for atoms in the given model
343    *
344    * @param sb
345    * @param model
346    * @param atomSpec
347    * @param alphaOnly
348    */
 
349  60 toggle protected void appendModel(StringBuilder sb, String model,
350    AtomSpecModel atomSpec, boolean alphaOnly)
351    {
352  60 sb.append("#").append(model).append(":");
353   
354  60 boolean firstPositionForModel = true;
355   
356  60 for (String chain : atomSpec.getChains(model))
357    {
358  94 chain = " ".equals(chain) ? chain : chain.trim();
359   
360  94 List<int[]> rangeList = atomSpec.getRanges(model, chain);
361  94 for (int[] range : rangeList)
362    {
363  119 appendRange(sb, range[0], range[1], chain, firstPositionForModel,
364    false);
365  119 firstPositionForModel = false;
366    }
367    }
368  60 if (alphaOnly)
369    {
370    /*
371    * restrict to alpha carbon, no alternative locations
372    * (needed to ensuring matching atom counts for superposition)
373    */
374    // TODO @P instead if RNA - add nucleotide flag to AtomSpecModel?
375  19 sb.append("@CA").append(NO_ALTLOCS);
376    }
377    }
378   
 
379  1 toggle @Override
380    public List<StructureCommandI> showBackbone()
381    {
382  1 return Arrays.asList(SHOW_BACKBONE);
383    }
384   
 
385  1 toggle @Override
386    public StructureCommandI loadFile(String file)
387    {
388  1 return new StructureCommand("open " + file);
389    }
390   
391    /**
392    * Overrides the default method to concatenate colour commands into one
393    */
 
394  2 toggle @Override
395    public List<StructureCommandI> colourBySequence(
396    Map<Object, AtomSpecModel> colourMap)
397    {
398  2 List<StructureCommandI> commands = new ArrayList<>();
399  2 StringBuilder sb = new StringBuilder(colourMap.size() * 20);
400  2 boolean first = true;
401  2 for (Object key : colourMap.keySet())
402    {
403  6 Color colour = (Color) key;
404  6 final AtomSpecModel colourData = colourMap.get(colour);
405  6 StructureCommandI command = getColourCommand(colourData, colour);
406  6 if (!first)
407    {
408  4 sb.append(getCommandSeparator());
409    }
410  6 first = false;
411  6 sb.append(command.getCommand());
412    }
413   
414  2 commands.add(new StructureCommand(sb.toString()));
415  2 return commands;
416    }
417   
 
418  1 toggle @Override
419    public StructureCommandI openSession(String filepath)
420    {
421    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/filetypes.html
422    // this version of the command has no dependency on file extension
423  1 return new StructureCommand("open chimera:" + filepath);
424    }
425   
 
426  1 toggle @Override
427    public StructureCommandI closeViewer()
428    {
429  1 return CLOSE_CHIMERA;
430    }
431   
 
432  1 toggle @Override
433    public List<StructureCommandI> startNotifications(String uri)
434    {
435    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/listen.html
436  1 List<StructureCommandI> cmds = new ArrayList<>();
437  1 cmds.add(new StructureCommand("listen start models url " + uri));
438  1 cmds.add(new StructureCommand("listen start select prefix SelectionChanged url " + uri));
439  1 return cmds;
440    }
441   
 
442  1 toggle @Override
443    public List<StructureCommandI> stopNotifications()
444    {
445  1 List<StructureCommandI> cmds = new ArrayList<>();
446  1 cmds.add(STOP_NOTIFY_MODELS);
447  1 cmds.add(STOP_NOTIFY_SELECTION);
448  1 return cmds;
449    }
450   
 
451  1 toggle @Override
452    public StructureCommandI getSelectedResidues()
453    {
454  1 return GET_SELECTION;
455    }
456   
 
457  1 toggle @Override
458    public StructureCommandI listResidueAttributes()
459    {
460  1 return LIST_RESIDUE_ATTRIBUTES;
461    }
462   
 
463  1 toggle @Override
464    public StructureCommandI getResidueAttributes(String attName)
465    {
466    // this alternative command
467    // list residues spec ':*/attName' attr attName
468    // doesn't report 'None' values (which is good), but
469    // fails for 'average.bfactor' (which is bad):
470  1 return new StructureCommand("list residues attr '" + attName + "'");
471    }
472   
473    }