From: Jim Procter Date: Wed, 15 Oct 2014 08:55:13 +0000 (+0100) Subject: JAL-674 JAL-1305 JAL-348 new liftOver method and test to allow annotation to be propa... X-Git-Tag: Jalview_2_9~169^2~29 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=85d8faf004199c5cc63773d7d4643143d16600da;p=jalview.git JAL-674 JAL-1305 JAL-348 new liftOver method and test to allow annotation to be propagated from one sequence to another via a sequence mapping --- diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index 601339a..92a4f6d 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -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 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 mapForsq = new Hashtable(); + if (sequenceMapping != null) + { + if (sp2sq != null) + { + for (Entry 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 index 0000000..7df6255 --- /dev/null +++ b/test/jalview/datamodel/AlignmentAnnotationTests.java @@ -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) + } + +}