Merge branch 'develop' into feature/JAL-3390hideUnmappedStructure
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 7 Aug 2019 13:19:17 +0000 (14:19 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 7 Aug 2019 13:19:17 +0000 (14:19 +0100)
Conflicts:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java

1  2 
gradle.properties
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/gui/JalviewChimeraBindingModel.java
src/jalview/structures/models/AAStructureBindingModel.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
utils/testnglibs/testng-sources.jar

@@@ -1,7 -1,7 +1,7 @@@
  jalviewDir = .
  
--#JAVA_VERSION = 1.8
--JAVA_VERSION = 11
++JAVA_VERSION = 1.8
++#JAVA_VERSION = 11
  JALVIEW_VERSION = DEVELOPMENT
  INSTALLATION = Source
  jalview_keystore = keys/.keystore
@@@ -1400,5 -1400,7 +1400,9 @@@ label.pca = PC
  label.create_image_of = Create {0} image of {1}
  label.click_to_edit = Click to edit, right-click for menu
  label.by_annotation_tooltip = Annotation Colour is configured from the main Colour menu
 +label.show_alignment_only = Show alignment only
- label.hide_hidden_regions = Hide hidden columns / sequences
++label.hide_hidden_regions = Hide hidden columns / sequences
+ label.show_linked_features = Show {0} features
+ label.on_top = on top
+ label.include_linked_features = Include {0} features
 -label.include_linked_tooltip = Include visible {0} features<br>converted to local sequence coordinates
++label.include_linked_tooltip = Include visible {0} features<br>converted to local sequence coordinates
@@@ -1402,5 -1401,7 +1402,9 @@@ label.pca = AC
  label.create_image_of = Crear imagen {0} de {1}
  label.click_to_edit = Haga clic para editar, clic en el botón derecho para ver el menú  
  label.by_annotation_tooltip = El color de anotación se configura desde el menú principal de colores
 +label.show_alignment_only = Mostrar solo alineamiento
- label.hide_hidden_regions = Ocultar columnas / secuencias ocultas
++label.hide_hidden_regions = Ocultar columnas / secuencias ocultas
+ label.show_linked_features = Características de {0}
+ label.on_top = encima
+ label.include_linked_features = Incluir características de {0}
 -label.include_linked_tooltip = Incluir características de {0}<br>convertidas a coordenadas de secuencia local
++label.include_linked_tooltip = Incluir características de {0}<br>convertidas a coordenadas de secuencia local
@@@ -254,8 -298,8 +261,8 @@@ public class ChimeraCommand
     * @param endPos
     * @param chain
     */
-   public static void addMapRange(Map<Object, AtomSpecModel> map,
-           Object key, int model, int startPos, int endPos, String chain)
 -  protected static void addAtomSpecRange(Map<Object, AtomSpecModel> map,
++  public static void addAtomSpecRange(Map<Object, AtomSpecModel> map,
+           Object value, int model, int startPos, int endPos, String chain)
    {
      /*
       * Get/initialize map of data for the colour
@@@ -858,320 -823,4 +858,321 @@@ public abstract class AAStructureBindin
  
    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 residues mapped to the alignment should be shown in the
 +   * structure viewer, else false
 +   * 
 +   * @return
 +   */
 +  public boolean isShowAlignmentOnly()
 +  {
 +    return showAlignmentOnly;
 +  }
 +
 +  /**
 +   * Sets the flag for hiding regions of structure which are hidden in the
 +   * alignment (only applies when the structure viewer is restricted to the
 +   * alignment only)
 +   * 
 +   * @param b
 +   */
 +  public void setHideHiddenRegions(boolean b)
 +  {
 +    hideHiddenRegions = b;
 +  }
 +
 +  /**
 +   * Answers true if regions hidden in the alignment should also be hidden in the
 +   * structure viewer, else false (only applies when the structure viewer is
 +   * restricted to the alignment only)
 +   * 
 +   * @return
 +   */
 +  public boolean isHideHiddenRegions()
 +  {
 +    return hideHiddenRegions;
 +  }
 +
 +  /**
 +   * 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
 +   * @param refocus
 +   *                         if true, refit the display to the viewer
 +   */
 +  public void showStructures(AlignViewportI alignViewportI, boolean refocus)
 +  {
 +    // override with implementation
 +  }
 +
 +  @Override
 +  public void updateColours(Object source)
 +  {
 +    AlignmentViewPanel ap = (AlignmentViewPanel) source;
 +    // ignore events from panels not used to colour this view
 +    if (!getViewer().isUsedforcolourby(ap))
 +    {
 +      return;
 +    }
 +    if (!isLoadingFromArchive())
 +    {
 +      updateStructureColours(ap);
 +    }
 +  }
 +
 +  /**
 +   * Sets the list of chains to display (as "pdbid:chain"), where an empty list
 +   * means show all
 +   * 
 +   * @param chains
 +   */
 +  public void setChainsToShow(List<String> chains)
 +  {
 +    chainsToShow = chains;
 +  }
 +
 +  /**
 +   * Answers true if the specified structure and chain are selected to be shown in
 +   * the viewer, else false
 +   * 
 +   * @param pdbId
 +   * @param chainId
 +   * @return
 +   */
 +  protected boolean isShowChain(String pdbId, String chainId)
 +  {
 +    if (chainsToShow.isEmpty())
 +    {
 +      return true;
 +    }
 +    return chainsToShow.contains(pdbId + ":" + chainId);
 +  }
 +
 +  @Override
 +  public abstract String[] getStructureFiles();
 +
 +  /**
 +   * Builds a model of residues mapped from sequences to show on structure, taking
 +   * into account user choices of
 +   * <ul>
 +   * <li>which chains are shown</li>
 +   * <li>whether all structure is shown, or only that mapped to the alignment</li>
 +   * <li>whether hidden regions of the alignment are hidden (excluded) or grayed
 +   * out (included)</li>
 +   * </ul>
 +   * 
 +   * @param av
 +   * @return
 +   */
 +  protected AtomSpecModel getShownResidues(AlignViewportI av)
 +  {
 +    AlignmentI alignment = av.getAlignment();
 +    final int width = alignment.getWidth();
 +  
 +    String[] files = getStructureFiles();
 +  
 +    AtomSpecModel model = new AtomSpecModel();
 +  
 +    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();
 +            if (!isShowChain(mapping.getPdbId(), chainCd))
 +            {
 +              continue;
 +            }
 +            Iterator<int[]> visible;
 +            if (isShowAlignmentOnly() && isHideHiddenRegions())
 +            {
 +              visible = alignment.getHiddenColumns()
 +                    .getVisContigsIterator(0, width, true);
 +            }
 +            else
 +            {
 +              visible = Collections.singletonList(new int[] { 0, width })
 +                      .iterator();
 +            }
 +            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())
 +              {
 +                for (int[] range : residueRanges)
 +                {
 +                  model.addRange(pdbfnum, range[0], range[1], chainCd);
 +                }
 +              }
 +            }
 +          }
 +        }
 +      }
 +    }
 +  
 +    return model;
 +  }
 +
 +  /**
 +   * Answers a default structure model specification which is simply the string
 +   * form of the model number. Override if needed to specify submodels.
 +   * 
 +   * @param model
 +   * @return
 +   */
 +  public String getModelSpec(int model)
 +  {
 +    return String.valueOf(model);
 +  }
 +
 +  /**
 +   * Build a data structure which records contiguous subsequences for each colour.
 +   * From this we can easily generate the Chimera command for colour by sequence.
 +   * 
 +   * <pre>
 +   * Color
 +   *     Model number
 +   *         Chain
 +   *             list of start/end ranges
 +   * </pre>
 +   * 
 +   * Ordering is by order of addition (for colours and positions), natural
 +   * ordering (for models and chains)
 +   * 
 +   * @param viewPanel
 +   * @return
 +   */
-   protected Map<Object, AtomSpecModel> buildColoursMap(
++  public Map<Object, AtomSpecModel> buildColoursMap(
 +          AlignmentViewPanel viewPanel)
 +  {
 +    FeatureRenderer fr = viewPanel.getFeatureRenderer();
 +    FeatureColourFinder finder = new FeatureColourFinder(fr);
 +    AlignViewportI viewport = viewPanel.getAlignViewport();
 +    HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
 +    AlignmentI al = viewport.getAlignment();
 +    SequenceRenderer sr = getSequenceRenderer(viewPanel);
 +    String[] files = getStructureFiles();
 +    
 +    Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<>();
 +    Color lastColour = null;
 +  
 +    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
 +    {
 +      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
 +  
 +      if (mapping == null || mapping.length < 1)
 +      {
 +        continue;
 +      }
 +  
 +      int startPos = -1, lastPos = -1;
 +      String lastChain = "";
 +      for (int s = 0; s < sequence[pdbfnum].length; s++)
 +      {
 +        for (int sp, m = 0; m < mapping.length; m++)
 +        {
 +          final SequenceI seq = sequence[pdbfnum][s];
 +          if (mapping[m].getSequence() == seq
 +                  && (sp = al.findIndex(seq)) > -1)
 +          {
 +            SequenceI asp = al.getSequenceAt(sp);
 +            for (int r = 0; r < asp.getLength(); r++)
 +            {
 +              // no mapping to gaps in sequence
 +              if (Comparison.isGap(asp.getCharAt(r)))
 +              {
 +                continue;
 +              }
 +              int pos = mapping[m].getPDBResNum(asp.findPosition(r));
 +  
 +              if (pos < 1 || pos == lastPos)
 +              {
 +                continue;
 +              }
 +  
 +              Color colour = sr.getResidueColour(seq, r, finder);
 +  
 +              /*
 +               * hidden regions are shown gray or, optionally, ignored
 +               */
 +              if (!cs.isVisible(r))
 +              {
 +                if (hideHiddenRegions)
 +                {
 +                  continue;
 +                }
 +                else
 +                {
 +                  colour = Color.GRAY;
 +                }
 +              }
 +  
 +              final String chain = mapping[m].getChain();
 +  
 +              /*
 +               * 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 = !colour.equals(lastColour);
 +              final boolean nonContig = lastPos + 1 != pos;
 +              final boolean newChain = !chain.equals(lastChain);
 +              if (newColour || nonContig || newChain)
 +              {
 +                if (startPos != -1)
 +                {
-                   ChimeraCommands.addMapRange(colourMap, lastColour,
++                  ChimeraCommands.addAtomSpecRange(colourMap, lastColour,
 +                          pdbfnum, startPos,
 +                          lastPos, lastChain);
 +                }
 +                startPos = pos;
 +              }
 +              lastColour = colour;
 +              lastPos = pos;
 +              lastChain = chain;
 +            }
 +            // final colour range
 +            if (lastColour != null)
 +            {
-               ChimeraCommands.addMapRange(colourMap, lastColour, pdbfnum,
++              ChimeraCommands.addAtomSpecRange(colourMap, lastColour,
++                      pdbfnum,
 +                      startPos, lastPos, lastChain);
 +            }
 +            // break;
 +          }
 +        }
 +      }
 +    }
 +    return colourMap;
 +  }
  }
@@@ -50,96 -45,8 +50,97 @@@ import java.util.Map
  import org.testng.annotations.BeforeClass;
  import org.testng.annotations.Test;
  
 +import junit.extensions.PA;
 +
  public class ChimeraCommandsTest
  {
 +  private SequenceRenderer sr;
++
++  private String[] files;
 +  
 +  private AAStructureBindingModel mockBinding = new AAStructureBindingModel(
 +          null, null)
 +  {
 +    @Override
 +    public void releaseReferences(Object svl)
 +    {
 +    }
 +
 +    @Override
 +    public void highlightAtoms(List<AtomSpec> atoms)
 +    {
 +    }
 +
 +    @Override
 +    public List<String> getChainNames()
 +    {
 +      return null;
 +    }
 +
 +    @Override
 +    public void setJalviewColourScheme(ColourSchemeI cs)
 +    {
 +    }
 +
 +    @Override
 +    public String superposeStructures(AlignmentI[] alignments,
 +            int[] structureIndices, HiddenColumns[] hiddenCols)
 +    {
 +      return null;
 +    }
 +
 +    @Override
 +    public void setBackgroundColour(Color col)
 +    {
 +    }
 +
 +    @Override
 +    protected String[] getColourBySequenceCommands(String[] files,
 +            AlignmentViewPanel avp)
 +    {
 +      return null;
 +    }
 +
 +    @Override
 +    public jalview.api.SequenceRenderer getSequenceRenderer(
 +            AlignmentViewPanel alignment)
 +    {
 +      return sr;
 +    }
 +
 +    @Override
 +    protected void colourBySequence(String[] colourBySequenceCommands)
 +    {
 +    }
 +
 +    @Override
 +    public void colourByChain()
 +    {
 +    }
 +
 +    @Override
 +    public void colourByCharge()
 +    {
 +    }
 +
 +    @Override
 +    public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
 +    {
 +      return null;
 +    }
 +
 +    @Override
 +    public String[] getStructureFiles()
 +    {
-       return null;
++      return files;
 +    }
 +
 +    @Override
 +    public String getModelSpec(int model)
 +    {
 +      return "#" + String.valueOf(model);
 +    }
 +  };
  
    @BeforeClass(alwaysRun = true)
    public void setUpJvOptionPane()
    @Test(groups = { "Functional" })
    public void testBuildColourCommands()
    {
--
 -    Map<Object, AtomSpecModel> map = new LinkedHashMap<Object, AtomSpecModel>();
 +    Map<Object, AtomSpecModel> map = new LinkedHashMap<>();
-     ChimeraCommands.addMapRange(map, Color.blue, 0, 2, 5, "A");
-     ChimeraCommands.addMapRange(map, Color.blue, 0, 7, 7, "B");
-     ChimeraCommands.addMapRange(map, Color.blue, 0, 9, 23, "A");
-     ChimeraCommands.addMapRange(map, Color.blue, 1, 1, 1, "A");
-     ChimeraCommands.addMapRange(map, Color.blue, 1, 4, 7, "B");
-     ChimeraCommands.addMapRange(map, Color.yellow, 1, 8, 8, "A");
-     ChimeraCommands.addMapRange(map, Color.yellow, 1, 3, 5, "A");
-     ChimeraCommands.addMapRange(map, Color.red, 0, 3, 5, "A");
-     ChimeraCommands.addMapRange(map, Color.red, 0, 6, 9, "A");
+     ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 2, 5, "A");
+     ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 7, 7, "B");
+     ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 9, 23, "A");
+     ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 1, 1, "A");
+     ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 4, 7, "B");
+     ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 8, 8, "A");
+     ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 3, 5, "A");
+     ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 3, 5, "A");
+     ChimeraCommands.addAtomSpecRange(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
       * start with just one feature/value...
       */
      featuresMap.put("chain", featureValues);
-     ChimeraCommands.addMapRange(featureValues, "X", 0, 8, 20, "A");
+     ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 8, 20, "A");
    
      List<String> commands = ChimeraCommands
 -            .buildSetAttributeCommands(featuresMap);
 +            .buildSetAttributeCommands(featuresMap, mockBinding);
      assertEquals(1, commands.size());
  
      /*
      assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:8-20.A");
  
      // add same feature value, overlapping range
-     ChimeraCommands.addMapRange(featureValues, "X", 0, 3, 9, "A");
+     ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 3, 9, "A");
      // same feature value, contiguous range
-     ChimeraCommands.addMapRange(featureValues, "X", 0, 21, 25, "A");
+     ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "A");
 -    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
 +    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap,
 +            mockBinding);
      assertEquals(1, commands.size());
      assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A");
  
      // same feature value and model, different chain
-     ChimeraCommands.addMapRange(featureValues, "X", 0, 21, 25, "B");
+     ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "B");
      // same feature value and chain, different model
-     ChimeraCommands.addMapRange(featureValues, "X", 1, 26, 30, "A");
+     ChimeraCommands.addAtomSpecRange(featureValues, "X", 1, 26, 30, "A");
 -    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
 +    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap,
 +            mockBinding);
      assertEquals(1, commands.size());
      assertEquals(commands.get(0),
              "setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A");
  
      // same feature, different value
-     ChimeraCommands.addMapRange(featureValues, "Y", 0, 40, 50, "A");
+     ChimeraCommands.addAtomSpecRange(featureValues, "Y", 0, 40, 50, "A");
 -    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
 +    commands = ChimeraCommands.buildSetAttributeCommands(featuresMap,
 +            mockBinding);
      assertEquals(2, commands.size());
      // commands are ordered by feature type but not by value
      // so use contains to test for the expected command:
      cs.addElement(4);
      af.getViewport().setColumnSelection(cs);
      af.hideSelColumns_actionPerformed(null);
 -    SequenceRenderer sr = new SequenceRenderer(af.getViewport());
 +    sr = new SequenceRenderer(af.getViewport());
      SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
--    String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
++    files = new String[] { "seq1.pdb", "seq2.pdb" };
      StructureSelectionManager ssm = new StructureSelectionManager();
  
      /*
              "B", map, null);
      ssm.addStructureMapping(sm2);
  
 -    StructureMappingcommandSet[] commands = ChimeraCommands
 -            .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel);
 +    /*
 +     * put data into the mock binding object
 +     */
 +    PA.setValue(mockBinding, "ssm", ssm);
 +    PA.setValue(mockBinding, "sequence", seqs);
 +
++    Map<Object, AtomSpecModel> colourMap = mockBinding
++            .buildColoursMap(af.alignPanel);
 +    String[] commands = ChimeraCommands
-             .getColourBySequenceCommand(files, af.alignPanel, mockBinding);
++            .getColourBySequenceCommand(colourMap, mockBinding);
      assertEquals(1, commands.length);
 -    assertEquals(1, commands[0].commands.length);
 -    String theCommand = commands[0].commands[0];
 +    String theCommand = commands[0];
      // M colour is #82827d (see strand.html help page)
      assertTrue(theCommand.contains("color #82827d #0:21.A|#1:21.B"));
      // H colour is #60609f
index 7a80303,7a80303..5e4c948
Binary files differ