JAL-2296 unit test for reading Chimera attributes to Jalview features
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 16 Nov 2016 17:01:59 +0000 (17:01 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 16 Nov 2016 17:01:59 +0000 (17:01 +0000)
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java

index 72b33cc..7e70fef 100644 (file)
@@ -1175,19 +1175,32 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
     String cmd = "list residues attr '" + attName + "'";
     List<String> residues = sendChimeraCommand(cmd, true);
-    /*
-     * Expect 0, 1 or more reply lines of the format (chi2 is attName):
-     * residue id #0:5.A chi2 -155.000836316 index 5
-     * or
-     * residue id #0:6.A chi3 None
-     * 
-     * We assume here that attributes on structure do not naturally convert
-     * to ranges on sequence, i.e. we just set one feature per mapped position.
-     * 
-     * To conflate positions, would need to first build a map 
-     * Map<String value, Map<Sequence seq, List<Integer position>>>
-     * and then traverse it to find feature ranges.
-     */
+
+    boolean featureAdded = createFeaturesForAttributes(attName, residues);
+    if (featureAdded)
+    {
+      alignmentPanel.getFeatureRenderer().featuresAdded();
+    }
+  }
+
+  /**
+   * Create features in Jalview for the given attribute name and structure
+   * residues.
+   * 
+   * <pre>
+   * The residue list should be 0, 1 or more reply lines of the format: 
+   *     residue id #0:5.A isHelix -155.000836316 index 5 
+   * or 
+   *     residue id #0:6.A isHelix None
+   * </pre>
+   * 
+   * @param attName
+   * @param residues
+   * @return
+   */
+  protected boolean createFeaturesForAttributes(String attName,
+          List<String> residues)
+  {
     boolean featureAdded = false;
     String featureGroup = getViewerFeatureGroup();
 
@@ -1236,6 +1249,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       spec.setPdbFile(pdbFile);
 
       List<AtomSpec> atoms = Collections.singletonList(spec);
+
+      /*
+       * locate the mapped position in the alignment (if any)
+       */
       SearchResults sr = getSsm()
               .findAlignmentPositionsForStructurePositions(atoms);
 
@@ -1255,10 +1272,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
         featureAdded |= seq.addSequenceFeature(sf);
       }
     }
-    if (featureAdded)
-    {
-      alignmentPanel.getFeatureRenderer().featuresAdded();
-    }
+    return featureAdded;
   }
 
   /**
index a31a065..449b219 100644 (file)
@@ -334,4 +334,141 @@ public class JalviewChimeraView
     chimeraViewer.closeViewer(true);
     chimeraViewer = null;
   }
+
+  /**
+   * Test for creating Jalview features from attributes on mapped residues in
+   * Chimera. Note this uses local copies of PDB and SIFTS file, no network
+   * connection required.
+   * 
+   * @throws IOException
+   * @throws SiftsException
+   */
+  // External as this requires a local install of Chimera
+  @Test(groups = { "External" })
+  public void testGetAttributes() throws IOException, SiftsException
+  {
+    String inFile = "examples/uniref50.fa";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
+            FormatAdapter.FILE);
+    assertNotNull(af, "Failed to create AlignFrame");
+    SequenceI fer2Arath = af.getViewport().getAlignment()
+            .findName("FER2_ARATH");
+    assertNotNull(fer2Arath, "Didn't find FER2_ARATH");
+  
+    /*
+     * need a Uniprot dbref for SIFTS mapping to work!!
+     */
+    fer2Arath.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
+  
+    /*
+     * use local test PDB and SIFTS files
+     */
+    String pdbFilePath = new File(
+            "test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
+    PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
+    String siftsFilePath = new File(
+            "test/jalview/ext/rbvi/chimera/4zho.xml.gz")
+            .getPath();
+    SiftsClient.setMockSiftsFile(new File(siftsFilePath));
+  
+    StructureViewer structureViewer = new StructureViewer(af.getViewport()
+            .getStructureSelectionManager());
+    chimeraViewer = structureViewer.viewStructures(pdbEntry,
+            new SequenceI[] { fer2Arath }, af.getCurrentView()
+                    .getAlignPanel());
+  
+    JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
+            .getBinding();
+    do
+    {
+      try
+      {
+        Thread.sleep(500);
+      } catch (InterruptedException e)
+      {
+      }
+    } while (!binding.isFinishedInit());
+  
+    assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
+  
+    assertEquals(binding.getPdbCount(), 1);
+  
+    /*
+     * 'perform' menu action to copy visible features to
+     * attributes in Chimera
+     */
+    // TODO rename and pull up method to binding interface
+    // once functionality is added for Jmol as well
+    binding.copyStructureAttributesToFeatures("isHelix", af.getViewport()
+            .getAlignPanel());
+
+    /*
+     * verify 22 residues have isHelix feature
+     * (may merge into ranges in future)
+     */
+    af.setShowSeqFeatures(true);
+    FeatureRenderer fr = af.getFeatureRenderer();
+    fr.setVisible("isHelix");
+    for (int res = 75; res <= 83; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
+    }
+    for (int res = 117; res <= 123; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
+    }
+    for (int res = 129; res <= 131; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
+    }
+    for (int res = 143; res <= 145; res++)
+    {
+      checkFeaturesAtRes(fer2Arath, fr, res, "isHelix");
+    }
+
+    /*
+     * fetch a numeric valued attribute
+     */
+    binding.copyStructureAttributesToFeatures("phi", af.getViewport()
+            .getAlignPanel());
+    fr.setVisible("phi");
+    List<SequenceFeature> fs = fr.findFeaturesAtRes(fer2Arath, 54);
+    assertEquals(fs.size(), 3);
+    assertEquals(fs.get(0).getType(), "RESNUM");
+    assertEquals(fs.get(1).getType(), "phi");
+    assertEquals(fs.get(2).getType(), "phi");
+    assertEquals(fs.get(1).getDescription(), "A"); // chain
+    assertEquals(fs.get(2).getDescription(), "B");
+    assertEquals(fs.get(1).getScore(), -131.0713f, 0.001f);
+    assertEquals(fs.get(2).getScore(), -127.39512, 0.001f);
+
+    /*
+     * tear down - also in AfterMethod
+     */
+    SiftsClient.setMockSiftsFile(null);
+    chimeraViewer.closeViewer(true);
+    chimeraViewer = null;
+  }
+
+  /**
+   * Helper method to verify new feature at a sequence position
+   * 
+   * @param seq
+   * @param fr
+   * @param res
+   * @param featureType
+   */
+  protected void checkFeaturesAtRes(SequenceI seq, FeatureRenderer fr,
+          int res, String featureType)
+  {
+    String where = "at position " + res;
+    List<SequenceFeature> fs = fr.findFeaturesAtRes(seq, res);
+    assertEquals(fs.size(), 2, where);
+    assertEquals(fs.get(0).getType(), "RESNUM", where);
+    SequenceFeature sf = fs.get(1);
+    assertEquals(sf.getType(), featureType, where);
+    assertEquals(sf.getFeatureGroup(), "Chimera", where);
+    assertEquals(sf.getDescription(), "True", where);
+    assertEquals(sf.getScore(), Float.NaN, where);
+  }
 }