JAL-674 JAL-1305 JAL-348 new liftOver method and test to allow annotation to be propa...
authorJim Procter <j.procter@dundee.ac.uk>
Wed, 15 Oct 2014 08:55:13 +0000 (09:55 +0100)
committerJim Procter <j.procter@dundee.ac.uk>
Wed, 15 Oct 2014 08:55:13 +0000 (09:55 +0100)
src/jalview/datamodel/AlignmentAnnotation.java
test/jalview/datamodel/AlignmentAnnotationTests.java [new file with mode: 0644]

index 601339a..92a4f6d 100755 (executable)
@@ -28,6 +28,8 @@ import jalview.analysis.WUSSParseException;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import fr.orsay.lri.varna.models.rna.RNA;
 
@@ -105,7 +107,7 @@ public class AlignmentAnnotation
     // System.out.println("featuregroup " + _rnasecstr[0].getFeatureGroup());
   }
 
-  public java.util.Hashtable sequenceMapping;
+  public java.util.Hashtable<Integer, Annotation> sequenceMapping;
 
   /** DOCUMENT ME!! */
   public float graphMin;
@@ -1117,4 +1119,45 @@ public class AlignmentAnnotation
   {
     return isrna;
   }
+
+  /**
+   * transfer annotation to the given sequence using the given mapping from the
+   * current positions or an existing sequence mapping
+   * 
+   * @param sq
+   * @param sp2sq
+   *          map involving sq as To or From
+   */
+  public void liftOver(SequenceI sq, Mapping sp2sq)
+  {
+    boolean mapIsTo = (sp2sq != null) ? (sp2sq.getTo() == sq || sp2sq
+            .getTo() == sq.getDatasetSequence()) : false;
+
+    // TODO build a better annotation element map and get rid of annotations[]
+    Hashtable<Integer, Annotation> mapForsq = new Hashtable();
+    if (sequenceMapping != null)
+    {
+      if (sp2sq != null)
+      {
+        for (Entry<Integer, Annotation> ie : sequenceMapping.entrySet())
+        {
+          Integer mpos = Integer.valueOf(mapIsTo ? sp2sq
+                  .getMappedPosition(ie.getKey()) : sp2sq.getPosition(ie
+                  .getKey()));
+          if (mpos >= sq.getStart() && mpos <= sq.getEnd())
+          {
+            mapForsq.put(mpos, ie.getValue());
+          }
+        }
+        sequenceMapping = mapForsq;
+        sequenceRef = sq;
+        adjustForAlignment();
+      }
+      else
+      {
+        // trim positions
+      }
+    }
+
+  }
 }
diff --git a/test/jalview/datamodel/AlignmentAnnotationTests.java b/test/jalview/datamodel/AlignmentAnnotationTests.java
new file mode 100644 (file)
index 0000000..7df6255
--- /dev/null
@@ -0,0 +1,153 @@
+package jalview.datamodel;
+
+import static org.junit.Assert.*;
+
+import java.awt.Frame;
+
+import javax.swing.JFrame;
+import javax.swing.SwingWorker;
+
+import jalview.analysis.AlignSeq;
+import jalview.gui.AlignFrame;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.FormatAdapter;
+
+import org.junit.Test;
+
+public class AlignmentAnnotationTests
+{
+  /**
+   * create some dummy annotation derived from the sequence
+   * 
+   * @param sq
+   */
+  public static void createAnnotation(SequenceI sq)
+  {
+    Annotation[] al = new Annotation[sq.getLength()];
+    for (int i = 0; i < al.length; i++)
+    {
+      al[i] = new Annotation(new Annotation("" + sq.getCharAt(i), "",
+              (char) 0, (float) sq.findPosition(i)));
+    }
+    AlignmentAnnotation alan = new AlignmentAnnotation("For "
+            + sq.getName(), "Fake alignment annot", al);
+    // create a sequence mapping for the annotation vector in its current state
+    alan.createSequenceMapping(sq, sq.getStart(), false);
+    sq.addAlignmentAnnotation(alan);
+  }
+
+  /**
+   * use this to test annotation derived from method above as it is transferred
+   * across different sequences derived from same dataset coordinate frame
+   * 
+   * @param ala
+   */
+  public static void testAnnotTransfer(AlignmentAnnotation ala)
+  {
+    assertEquals(
+            "Failed - need annotation created by createAnnotation method",
+            ala.description, "Fake alignment annot");
+    ala.adjustForAlignment();
+    for (int p = 0; p < ala.annotations.length; p++)
+    {
+      if (ala.annotations[p] != null)
+      {
+        assertEquals("Mismatch at position " + p
+                + " between annotation position value and sequence"
+                + ala.annotations[p], (int) ala.annotations[p].value,
+                ala.sequenceRef.findPosition(p));
+      }
+    }
+  }
+
+  /**
+   * Tests the liftOver method and also exercises the functions for remapping
+   * annotation across different reference sequences. Here, the test is between
+   * different dataset frames (annotation transferred by mapping between
+   * sequences)
+   */
+  @Test
+  public void testLiftOver()
+  {
+    SequenceI sqFrom = new Sequence("fromLong", "QQQCDEWGH");
+    sqFrom.setStart(10);
+    sqFrom.setEnd(sqFrom.findPosition(sqFrom.getLength() - 1));
+    SequenceI sqTo = new Sequence("toShort", "RCDEW");
+    sqTo.setStart(20);
+    sqTo.setEnd(sqTo.findPosition(sqTo.getLength() - 1));
+    createAnnotation(sqTo);
+    AlignmentAnnotation origTo = sqTo.getAnnotation()[0];
+    createAnnotation(sqFrom);
+    AlignmentAnnotation origFrom = sqFrom.getAnnotation()[0];
+    AlignSeq align = AlignSeq.doGlobalNWAlignment(sqFrom, sqTo,
+            AlignSeq.PEP);
+    SequenceI alSeq1 = new Sequence(sqFrom.getName(), align.getAStr1());
+    alSeq1.setStart(sqFrom.getStart() + align.getSeq1Start() - 1);
+    alSeq1.setEnd(sqFrom.getStart() + align.getSeq1End() - 1);
+    alSeq1.setDatasetSequence(sqFrom);
+    SequenceI alSeq2 = new Sequence(sqTo.getName(), align.getAStr2());
+    alSeq2.setStart(sqTo.getStart() + align.getSeq2Start() - 1);
+    alSeq2.setEnd(sqTo.getStart() + align.getSeq2End() - 1);
+    alSeq2.setDatasetSequence(sqTo);
+    System.out.println(new AppletFormatAdapter().formatSequences("STH",
+            new Alignment(new SequenceI[]
+            { sqFrom, alSeq1, sqTo, alSeq2 }), true));
+
+    Mapping mp = align.getMappingFromS1(false);
+
+    AlignmentAnnotation almap1 = new AlignmentAnnotation(
+            sqTo.getAnnotation()[0]);
+    almap1.liftOver(sqFrom, mp);
+    assertEquals(almap1.sequenceRef, sqFrom);
+    alSeq1.addAlignmentAnnotation(almap1);
+    almap1.setSequenceRef(alSeq1);
+    almap1.adjustForAlignment();
+    AlignmentAnnotation almap2 = new AlignmentAnnotation(
+            sqFrom.getAnnotation()[0]);
+    almap2.liftOver(sqTo, mp);
+    assertEquals(almap2.sequenceRef, sqTo);
+
+    alSeq2.addAlignmentAnnotation(almap2);
+    almap2.setSequenceRef(alSeq2);
+    almap2.adjustForAlignment();
+
+    AlignmentI all = new Alignment(new SequenceI[]
+    { alSeq1, alSeq2 });
+    all.addAnnotation(almap1);
+    all.addAnnotation(almap2);
+    System.out.println(new AppletFormatAdapter().formatSequences("STH",
+            all, true));
+
+    for (int p = 0; p < alSeq1.getLength(); p++)
+    {
+      Annotation orig1, trans1, orig2, trans2;
+      trans2 = almap2.annotations[p];
+      orig2 = origFrom.annotations[alSeq1.findPosition(p)
+              - sqFrom.getStart()];
+      orig1 = origTo.annotations[alSeq2.findPosition(p) - sqTo.getStart()];
+      trans1 = almap1.annotations[p];
+      if (trans1 == trans2)
+      {
+        System.out.println("Pos " + p + " mismatch");
+        continue;
+      }
+      assertEquals(
+              "Mismatch on Original From and transferred annotation on 2",
+              (orig2 != null) ? orig2.toString() : null,
+              (trans2 != null) ? trans2.toString() : null);
+      assertEquals(
+              "Mismatch on Original To and transferred annotation on 1",
+              (orig1 != null) ? orig1.toString() : null,
+              (trans1 != null) ? trans1.toString() : null);
+      String alm1 = ""
+              + (almap1.annotations.length > p ? almap1.annotations[p].displayCharacter
+                      : "Out of range");
+      String alm2 = ""
+              + (almap2.annotations.length > p ? almap2.annotations[p].displayCharacter
+                      : "Out of range");
+      assertEquals("Position " + p + " " + alm1 + " " + alm2, alm1, alm2);
+    }
+    // new jalview.io.FormatAdapter().formatSequences("STOCKHOLM", n)
+  }
+
+}