diff --git a/src/main/java/picard/sam/AbstractAlignmentMerger.java b/src/main/java/picard/sam/AbstractAlignmentMerger.java index 3abd4e13db..708f5e06a1 100644 --- a/src/main/java/picard/sam/AbstractAlignmentMerger.java +++ b/src/main/java/picard/sam/AbstractAlignmentMerger.java @@ -791,7 +791,7 @@ protected static void clipForOverlappingReads(final SAMRecord read1, final SAMRe } } - private static int getReadPositionAtReferencePositionIgnoreSoftClips(final SAMRecord rec, final int pos) { + static int getReadPositionAtReferencePositionIgnoreSoftClips(final SAMRecord rec, final int pos) { final int readPosition; final Cigar oldCigar = rec.getCigar(); final int oldStart = rec.getAlignmentStart(); @@ -819,10 +819,12 @@ private static int getReadPositionAtReferencePositionIgnoreSoftClips(final SAMRe // Temporarily use the newCigar that has SOFT_CLIPs replaced with MATCH_OR_MISMATCH to get read position at reference, but ignore existence of soft-clips rec.setCigar(newCigar); - readPosition = SAMRecord.getReadPositionAtReferencePosition(rec, pos, false); + // Since the read effectively got shifted forward by turning the clips into matches, the query position needs + // also to be moved forward bye posShift so that it's still querying the same base. + readPosition = SAMRecord.getReadPositionAtReferencePosition(rec, pos + posShift, false); rec.setCigar(oldCigar); - // instead of setting back the position of the read by posShift, which could create a negative start position, we add posShift the final position in the read. - return readPosition + posShift; + + return readPosition; } private static void clip3PrimeEndOfRead(final SAMRecord rec, final int clipFrom, final boolean useHardClipping) { diff --git a/src/test/java/picard/sam/AbstractAlignmentMergerTest.java b/src/test/java/picard/sam/AbstractAlignmentMergerTest.java index f0acb06d08..89bc09d56f 100644 --- a/src/test/java/picard/sam/AbstractAlignmentMergerTest.java +++ b/src/test/java/picard/sam/AbstractAlignmentMergerTest.java @@ -250,5 +250,29 @@ private static File newTempSamFile(final String filename) throws IOException { file.deleteOnExit(); return file; } + @DataProvider(name = "readPositionIgnoringSoftClips") + public Object[][] readPositionIgnoringSoftClips() { + return new Object[][] { + {"26S58M62S", 3688, 3827, 0}, // This is from the read that made us aware of a bug + {"26S58M62S", 3688, 3665, 4}, + {"26S58M62S", 3688, 3660, 0}, // Before soft clip + {"10S100M2S", 5, 10, 16}, + {"10S100M2S", 5, 3, 9}, + {"10S100M2S", 10, 12, 13}, + {"10S100M2S", 5, 107, 0} + }; + } + @Test(dataProvider = "readPositionIgnoringSoftClips") + public void testGetReadPositionIgnoringSoftClips(final String cigarString, final int startPosition, final int queryPosition, final int expectedReadPosititon) { + final SAMFileHeader newHeader = SAMRecordSetBuilder.makeDefaultHeader(SAMFileHeader.SortOrder.queryname, 100000,false); + final SAMRecord rec = new SAMRecord(newHeader); + + rec.setCigarString(cigarString); + rec.setAlignmentStart(startPosition); + + final int readPosition = AbstractAlignmentMerger.getReadPositionAtReferencePositionIgnoreSoftClips(rec, queryPosition); + + Assert.assertEquals(readPosition, expectedReadPosititon); + } }