Skip to content

Commit

Permalink
Added alignment type to RecordAndOffset class (#1411)
Browse files Browse the repository at this point in the history
* Added alignment type to RecordAndOffset class

- This was suggested as part of broadinstitute/picard#1370
- necessary to determine whether a RecordAndOffset object is an
(alignment) match, insertion, or deletion when queried by a
SamLocusIterator
- default behavior is preserved by overloading the default constructor

* Added tests for including the alignment type in RecordAndOffset
  • Loading branch information
michaelgatzen authored and lbergelson committed Aug 21, 2019
1 parent 2a6e2c2 commit 9628c1e
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 2 deletions.
37 changes: 37 additions & 0 deletions src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@
*/
public class AbstractRecordAndOffset {

/**
* Classifies whether the given event is a match, insertion, or deletion. This is deliberately not expressed
* as CIGAR operators, since there is no knowledge of the CIGAR string at the time that this is determined.
*/
public enum AlignmentType {
Match,
Insertion,
Deletion,
}

/**
* A SAMRecord aligned to reference position
*/
Expand All @@ -46,13 +56,32 @@ public class AbstractRecordAndOffset {
*/
protected final int offset;

/**
* The {@link AlignmentType} of this object, which classifies whether the given event is a match, insertion, or
* deletion when queried from a {@link SamLocusIterator}.
*/
protected final AlignmentType alignmentType;

/**
* @param record inner SAMRecord
* @param offset from the start of the read
*/
public AbstractRecordAndOffset(final SAMRecord record, final int offset) {
this.offset = offset;
this.record = record;
this.alignmentType = AlignmentType.Match;
}

/**
* @param record inner SAMRecord
* @param offset from the start of the read
* @param alignmentType The {@link AlignmentType} of this object, which is used when queried in
* a {@link SamLocusIterator}.
*/
public AbstractRecordAndOffset(final SAMRecord record, final int offset, final AlignmentType alignmentType) {
this.offset = offset;
this.record = record;
this.alignmentType = alignmentType;
}

/**
Expand All @@ -69,6 +98,14 @@ public SAMRecord getRecord() {
return record;
}

/**
* The {@link AlignmentType} of this object, which classifies whether the given event is a match, insertion, or
* deletion when queried from a {@link SamLocusIterator}.
*/
public AlignmentType getAlignmentType() {
return alignmentType;
}

/**
* @return the read base according to <code>offset</code>.
*/
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/htsjdk/samtools/util/SamLocusIterator.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,16 @@ public static class RecordAndOffset extends AbstractRecordAndOffset {
public RecordAndOffset(final SAMRecord record, final int offset) {
super(record, offset);
}

/**
* @param record inner <code>SAMRecord</code>
* @param offset 0-based offset from the start of <code>SAMRecord</code>
* @param alignmentType The {@link AlignmentType} of this object, which is used when queried in
* a {@link SamLocusIterator}.
*/
public RecordAndOffset(final SAMRecord record, final int offset, final AlignmentType alignmentType) {
super(record, offset, alignmentType);
}
}

/**
Expand Down Expand Up @@ -234,7 +244,7 @@ public void addDeleted(final SAMRecord read, int previousPosition) {
if (deletedInRecord == null) {
deletedInRecord = new ArrayList<>();
}
deletedInRecord.add(new RecordAndOffset(read, previousPosition));
deletedInRecord.add(new RecordAndOffset(read, previousPosition, AbstractRecordAndOffset.AlignmentType.Deletion));
}

/**
Expand All @@ -247,7 +257,7 @@ public void addInserted(final SAMRecord read, int firstPosition) {
if (insertedInRecord == null) {
insertedInRecord = new ArrayList<>();
}
insertedInRecord.add(new RecordAndOffset(read, firstPosition));
insertedInRecord.add(new RecordAndOffset(read, firstPosition, AbstractRecordAndOffset.AlignmentType.Insertion));
}

public List<RecordAndOffset> getDeletedInRecord() {
Expand Down
88 changes: 88 additions & 0 deletions src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public void testBasicIterator() {
for (final SamLocusIterator.LocusInfo li : sli) {
Assert.assertEquals(li.getPosition(), pos++);
Assert.assertEquals(li.getRecordAndOffsets().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), coverage);
// make sure that we are not accumulating indels
Assert.assertEquals(li.getDeletedInRecord().size(), 0);
Expand All @@ -87,6 +91,10 @@ public void testMissingQualityString() {
for (final SamLocusIterator.LocusInfo li : sli) {
Assert.assertEquals(li.getPosition(), pos++);
Assert.assertEquals(li.getRecordAndOffsets().size(), 2);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), 2);
}
}
Expand Down Expand Up @@ -124,6 +132,10 @@ public void testEmitUncoveredLoci() {
expectedReads = 0;
}
Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReads);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), expectedReads);
// make sure that we are not accumulating indels
Assert.assertEquals(li.getDeletedInRecord().size(), 0);
Expand Down Expand Up @@ -162,6 +174,10 @@ public void testQualityFilter() {
int pos = startPosition;
for (final SamLocusIterator.LocusInfo li : sli) {
Assert.assertEquals(li.getRecordAndOffsets().size(), (pos % 2 == 0) ? coverage / 2 : coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), (pos % 2 == 0) ? coverage / 2 : coverage);
Assert.assertEquals(li.getPosition(), pos++);
// make sure that we are not accumulating indels
Expand Down Expand Up @@ -205,10 +221,18 @@ public void testSimpleDeletion() {

// make sure that we are accumulating indels
Assert.assertEquals(li.getDeletedInRecord().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getDeletedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Deletion);
}
Assert.assertEquals(li.getInsertedInRecord().size(), 0);
} else {
// make sure we are accumulating normal coverage
Assert.assertEquals(li.getRecordAndOffsets().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), coverage);

// make sure that we are not accumulating indels
Expand Down Expand Up @@ -242,12 +266,20 @@ public void testSimpleInsertion() {
Assert.assertEquals(li.getPosition(), pos++);
// make sure we are accumulating normal coverage
Assert.assertEquals(li.getRecordAndOffsets().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), coverage);

// make sure that we are not accumulating deletions
Assert.assertEquals(li.getDeletedInRecord().size(), 0);
if (incIndels && li.getPosition() == insStart) {
Assert.assertEquals(li.getInsertedInRecord().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getInsertedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Insertion);
}
} else {
Assert.assertEquals(li.getInsertedInRecord().size(), 0);
}
Expand Down Expand Up @@ -281,12 +313,20 @@ public void testStartWithInsertion() {
Assert.assertEquals(li.getPosition(), pos);
// accumulation of coverage
Assert.assertEquals(li.getRecordAndOffsets().size(), (indelPosition) ? 0 : coverage + 1);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), (indelPosition) ? 0 : coverage + 1);

// no accumulation of deletions
Assert.assertEquals(li.getDeletedInRecord().size(), 0);
// accumulation of insertion
Assert.assertEquals(li.getInsertedInRecord().size(), (indelPosition) ? coverage : 0);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getInsertedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Insertion);
}
// check offsets of the insertion
if (indelPosition) {
Assert.assertEquals(li.getInsertedInRecord().get(0).getOffset(), 0);
Expand Down Expand Up @@ -322,11 +362,19 @@ public void testStartWithSoftClipAndInsertion() {
Assert.assertEquals(li.getPosition(), pos);
// accumulation of coverage
Assert.assertEquals(li.getRecordAndOffsets().size(), (indelPosition) ? 0 : coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), (indelPosition) ? 0 : coverage);
// no accumulation of deletions
Assert.assertEquals(li.getDeletedInRecord().size(), 0);
// accumulation of insertion
Assert.assertEquals(li.getInsertedInRecord().size(), (indelPosition) ? coverage : 0);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getInsertedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Insertion);
}
// check offsets of the insertion
if (indelPosition) {
Assert.assertEquals(li.getInsertedInRecord().get(0).getOffset(), 1);
Expand Down Expand Up @@ -367,11 +415,19 @@ public void testNBeforeInsertion() {
Assert.assertEquals(li.getPosition(), pos);
// accumulation of coverage
Assert.assertEquals(li.getRecordAndOffsets().size(), (pos == endN) ? 0 : coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), (pos == endN) ? 0 : coverage);
// no accumulation of deletions
Assert.assertEquals(li.getDeletedInRecord().size(), 0);
// accumulation of insertion
Assert.assertEquals(li.getInsertedInRecord().size(), (pos == endN) ? coverage : 0);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getInsertedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Insertion);
}
// check offsets of the insertion
if (pos == endN) {
Assert.assertEquals(li.getInsertedInRecord().get(0).getOffset(), 2);
Expand Down Expand Up @@ -419,9 +475,17 @@ public void testNBeforeDeletion() {
Assert.assertEquals(li.getPosition(), pos);
// accumulation of coverage
Assert.assertEquals(li.getRecordAndOffsets().size(), (insideDeletion) ? 0 : coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), coverage); // either will be all deletions, or all non-deletions, but always of size `coverage`.
// accumulation of deletions
Assert.assertEquals(li.getDeletedInRecord().size(), (insideDeletion) ? coverage : 0);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getDeletedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Deletion);
}
// no accumulation of insertion
Assert.assertEquals(li.getInsertedInRecord().size(), 0);
// check offsets of the insertion
Expand Down Expand Up @@ -498,6 +562,10 @@ public void testSimpleGappedAlignment() {
if (incIndels && li.getPosition() == expectedInsertionPosition) {
// check the accumulated coverage
Assert.assertEquals(li.getInsertedInRecord().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getInsertedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Insertion);
}
// check the record offset
Assert.assertEquals(li.getInsertedInRecord().get(0).getOffset(), expectedInsertionOffset);
Assert.assertEquals(li.getInsertedInRecord().get(1).getOffset(), expectedInsertionOffset);
Expand All @@ -508,6 +576,10 @@ public void testSimpleGappedAlignment() {
if (inDelRange) {
// check the coverage for insertion and normal records
Assert.assertEquals(li.getDeletedInRecord().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getDeletedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Deletion);
}
Assert.assertEquals(li.getRecordAndOffsets().size(), 0);
Assert.assertEquals(li.size(), coverage); // includes deletions
// check the offset for the deletion
Expand All @@ -516,6 +588,10 @@ public void testSimpleGappedAlignment() {
} else {
// if it is not a deletion, perform the same test as before
Assert.assertEquals(li.getRecordAndOffsets().size(), coverage);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
Assert.assertEquals(li.size(), coverage);
// Assert.assertEquals(li.getDeletedInRecord().size(), 0);
Assert.assertEquals(li.getRecordAndOffsets().get(0).getOffset(), expectedReadOffsets[i]);
Expand Down Expand Up @@ -582,6 +658,10 @@ public void testOverlappingGappedAlignmentsWithoutIndels() {
Assert.assertEquals(li.size(), expectedDepths[i]);
Assert.assertEquals(li.getPosition(), expectedReferencePositions[i]);
Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReadOffsets[i].length);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
for (int j = 0; j < expectedReadOffsets[i].length; ++j) {
Assert.assertEquals(li.getRecordAndOffsets().get(j).getOffset(), expectedReadOffsets[i][j]);
}
Expand Down Expand Up @@ -658,11 +738,19 @@ public void testOverlappingGappedAlignmentsWithIndels() {
Assert.assertEquals(li.size(), expectedDepths[i] + expectedDelDepths[i]); // include deletions
Assert.assertEquals(li.getPosition(), expectedReferencePositions[i]);
Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReadOffsets[i].length);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getRecordAndOffsets()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Match);
}
for (int j = 0; j < expectedReadOffsets[i].length; ++j) {
Assert.assertEquals(li.getRecordAndOffsets().get(j).getOffset(), expectedReadOffsets[i][j]);
}
// check the deletions
Assert.assertEquals(li.getDeletedInRecord().size(), expectedDelDepths[i]);
// Check the correct assignment of the alignment type
for (final SamLocusIterator.RecordAndOffset rao : li.getDeletedInRecord()) {
Assert.assertEquals(rao.getAlignmentType(), SamLocusIterator.RecordAndOffset.AlignmentType.Deletion);
}
if (expectedDelDepths[i] != 0) {
Assert.assertEquals(li.getDeletedInRecord().get(0).getOffset(), expectedDelOffset);
}
Expand Down

0 comments on commit 9628c1e

Please sign in to comment.