Skip to content

Commit

Permalink
Fixes for Entity Viewer Variant view (#1109)
Browse files Browse the repository at this point in the history
- Fixed rendering of the genomic sequence for variants starting on either end of the sequence (first or last nucleotide)
- Updated calculations of the locations of both exons and the variant marker in the CDS diagram
  • Loading branch information
azangru authored Apr 2, 2024
1 parent f8c2367 commit 9451bb0
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,24 @@ const EXON_BLOCK_OFFSET_TOP = (VARIANT_MARKER_HEIGHT - EXON_BLOCK_HEIGHT) / 2;
const TranscriptVariantCDS = (props: Props) => {
const { exons, allele, cds } = props;

const scale = scaleLinear()
// length's domain is between 0 and the length of the CDS
const lengthScale = scaleLinear()
.domain([0, cds.nucleotide_length])
.range([0, DIAGRAM_WIDTH])
.interpolate(interpolateRound)
.clamp(true);

// meanwhile, since position starts at 1, the position scale is between 1 and the length of the CDS
const positionScale = scaleLinear()
.domain([1, cds.nucleotide_length])
.range([0, DIAGRAM_WIDTH])
.interpolate(interpolateRound)
.clamp(true);

const exonsWithWidths = getExonWidths({
exons,
containerWidth: DIAGRAM_WIDTH,
scale
scale: lengthScale
});

return (
Expand All @@ -95,7 +103,7 @@ const TranscriptVariantCDS = (props: Props) => {
<VariantMark
exons={exonsWithWidths}
allele={allele}
scale={scale}
scale={positionScale}
containerWidth={DIAGRAM_WIDTH}
/>
</svg>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type Props = {
variantToTranscriptEndDistance: number;
};

const SEQUENCE_LETTER_WIDTH = 16;
const SEQUENCE_LETTER_RIGHT_MARGIN = 1;

const TranscriptVariantGenomicSequence = (props: Props) => {
const {
leftFlankingSequence,
Expand All @@ -49,12 +52,21 @@ const TranscriptVariantGenomicSequence = (props: Props) => {
alleleType
} = props;

const leftOffsetInLetters = calculateLeftOffset({
flankingSequence: leftFlankingSequence,
variantSequence
});
const leftOffsetInPx = leftOffsetInLetters
? leftOffsetInLetters * SEQUENCE_LETTER_WIDTH +
leftOffsetInLetters * SEQUENCE_LETTER_RIGHT_MARGIN
: undefined;
const leftOffset = leftOffsetInPx ? `${leftOffsetInPx}px` : undefined;

return (
<div className={styles.diagramContainer}>
<div>
<div style={{ marginLeft: leftOffset }}>
<LeftFlankingSequence
flankingSequence={leftFlankingSequence}
variantSequence={variantSequence}
variantToTranscriptStartDistance={variantToTranscriptStartDistance}
/>
<ReferenceAlleleSequence sequence={variantSequence} />
Expand All @@ -75,19 +87,11 @@ const TranscriptVariantGenomicSequence = (props: Props) => {

const LeftFlankingSequence = ({
flankingSequence,
variantSequence,
variantToTranscriptStartDistance
}: {
flankingSequence: string;
variantSequence: string;
variantToTranscriptStartDistance: number;
}) => {
const leftOffset = calculateLeftOffset({
flankingSequence,
variantSequence
});
const marginLeft = leftOffset ? `${leftOffset}px` : undefined;

const letters = flankingSequence.split('');

const letterClasses = classNames(
Expand All @@ -112,7 +116,6 @@ const LeftFlankingSequence = ({
letter={letter}
key={index}
className={hasEllipsis && index === 0 ? ellipsisClasses : letterClasses}
style={index === 0 && marginLeft ? { marginLeft } : undefined}
/>
));
};
Expand Down Expand Up @@ -345,7 +348,7 @@ const getAltAlleleAnchorPosition = ({
refAlleleSequence: string;
variantType: string;
}) => {
const letterWidth = 16;
const letterWidth = SEQUENCE_LETTER_WIDTH;
const halfGenomicSequenceLength = Math.ceil(
DISPLAYED_REFERENCE_SEQUENCE_LENGTH / 2
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ describe('functions splitting genomic sequence into parts', () => {
).toBe(expectedVariantSeq);
});

test('Insertion, near forward-strand start, forward strand', () => {
test('insertion, near forward-strand start, forward strand', () => {
const sequence = 'GCGGGGTCCAGCAGGCAGGCTCCGGCCGTG'; // 30 characters

const expectedLeftFlankingSeq = 'GCGGGGTCC'; // first 9 characters
Expand Down Expand Up @@ -529,7 +529,7 @@ describe('functions splitting genomic sequence into parts', () => {
).toBe(expectedVariantSeq);
});

test('Insertion, near forward-strand start, reverse strand', () => {
test('insertion, near forward-strand start, reverse strand', () => {
const sequence = 'GCGGGGTCCAGCAGGCAGGCTCCGGCCGTG'; // 30 characters

const expectedLeftFlankingSeq = getReverseComplement(
Expand Down Expand Up @@ -563,6 +563,39 @@ describe('functions splitting genomic sequence into parts', () => {
})
).toBe(expectedVariantSeq);
});

test('allele starts at the start of genomic slice, reverse strand', () => {
const sequence = 'TCTCTCACACAGTAATACATG'; // 21 characters

const expectedLeftFlankingSeq = getReverseComplement(sequence).slice(0, 20);
const expectedVariantSeq = getReverseComplement(sequence)[20];
const expectedRightFlankingSeq = '';

expect(
getLeftFlankingGenomicSequence({
sequence,
distanceToSliceStart: 0,
distanceToSliceEnd: 20,
strand: 'reverse'
})
).toBe(expectedLeftFlankingSeq);
expect(
getRightFlankingGenomicSequence({
sequence,
distanceToSliceStart: 0,
distanceToSliceEnd: 20,
strand: 'reverse'
})
).toBe(expectedRightFlankingSeq);
expect(
getReferenceAlleleGenomicSequence({
sequence,
distanceToSliceStart: 0,
distanceToSliceEnd: 20,
strand: 'reverse'
})
).toBe(expectedVariantSeq);
});
});

describe('getProteinSliceCoordinates', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,9 +412,11 @@ export const getLeftFlankingGenomicSequence = ({
strand: 'forward' | 'reverse';
}) => {
if (strand === 'forward') {
return sequence.slice(0, distanceToSliceStart);
return distanceToSliceStart ? sequence.slice(0, distanceToSliceStart) : '';
} else {
return getReverseComplement(sequence).slice(0, distanceToSliceEnd);
return distanceToSliceEnd
? getReverseComplement(sequence).slice(0, distanceToSliceEnd)
: '';
}
};

Expand All @@ -430,9 +432,11 @@ export const getRightFlankingGenomicSequence = ({
strand: 'forward' | 'reverse';
}) => {
if (strand === 'forward') {
return sequence.slice(-1 * distanceToSliceEnd);
return distanceToSliceEnd ? sequence.slice(-1 * distanceToSliceEnd) : '';
} else {
return getReverseComplement(sequence).slice(-1 * distanceToSliceStart);
return distanceToSliceStart
? getReverseComplement(sequence).slice(-1 * distanceToSliceStart)
: '';
}
};

Expand Down
10 changes: 10 additions & 0 deletions src/shared/helpers/exon-helpers/exonHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as example1 from './fixtures/exons-cds-data-1';
import * as example2 from './fixtures/exons-cds-data-2';
import * as example3 from './fixtures/exons-cds-data-3';
import * as example4 from './fixtures/exons-cds-data-4';
import * as example5 from './fixtures/exons-cds-data-5';

describe('addRelativeLocationInCDSToExons', () => {
test('transcript with a single exon and no UTRs ', () => {
Expand Down Expand Up @@ -68,4 +69,13 @@ describe('addRelativeLocationInCDSToExons', () => {

expect(result).toEqual(example4.exonsWithRelativeLocationInCDS);
});

test('transcript with two exons and no UTRs', () => {
const result = addRelativeLocationInCDSToExons({
exons: example5.exons,
cds: example5.cds
});

expect(result).toEqual(example5.exonsWithRelativeLocationInCDS);
});
});
6 changes: 3 additions & 3 deletions src/shared/helpers/exon-helpers/exonHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const addRelativeLocationInCDSToExons = <
const cds = params.cds;
const exons = structuredClone(params.exons);

let lastPositionInCDS = 1;
let lastPositionInCDS = 0;

for (const exon of exons) {
if (doesExonIncludeCDS({ exon, cds })) {
Expand Down Expand Up @@ -86,7 +86,7 @@ export const addRelativeLocationInCDSToExons = <
!isExonEndWithinCDS({ exon, cds })
) {
// last exon in CDS
const relativeStart = lastPositionInCDS;
const relativeStart = lastPositionInCDS + 1;
const remainingExonLength =
cds.relative_end - exon.relative_location.start + 1;
const relativeEnd = relativeStart + remainingExonLength - 1;
Expand All @@ -98,7 +98,7 @@ export const addRelativeLocationInCDSToExons = <
};
} else {
// exon fully within CDS
const relativeStart = lastPositionInCDS;
const relativeStart = lastPositionInCDS + 1;
const exonLength =
exon.relative_location.end - exon.relative_location.start + 1;
const relativeEnd = relativeStart + exonLength - 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ export const exonsWithRelativeLocationInCDS = [
{
index: 2,
relative_location: { start: 4257, end: 6314 },
relative_location_in_cds: { start: 406, end: 851, length: 446 }
relative_location_in_cds: { start: 407, end: 852, length: 446 }
}
];
38 changes: 19 additions & 19 deletions src/shared/helpers/exon-helpers/fixtures/exons-cds-data-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,97 +244,97 @@ export const exonsWithRelativeLocationInCDS = [
{
index: 3,
relative_location: { start: 3570, end: 3818 },
relative_location_in_cds: { start: 67, end: 315, length: 249 }
relative_location_in_cds: { start: 68, end: 316, length: 249 }
},
{
index: 4,
relative_location: { start: 9569, end: 9677 },
relative_location_in_cds: { start: 315, end: 423, length: 109 }
relative_location_in_cds: { start: 317, end: 425, length: 109 }
},
{
index: 5,
relative_location: { start: 10594, end: 10643 },
relative_location_in_cds: { start: 423, end: 472, length: 50 }
relative_location_in_cds: { start: 426, end: 475, length: 50 }
},
{
index: 6,
relative_location: { start: 10735, end: 10775 },
relative_location_in_cds: { start: 472, end: 512, length: 41 }
relative_location_in_cds: { start: 476, end: 516, length: 41 }
},
{
index: 7,
relative_location: { start: 10992, end: 11106 },
relative_location_in_cds: { start: 512, end: 626, length: 115 }
relative_location_in_cds: { start: 517, end: 631, length: 115 }
},
{
index: 8,
relative_location: { start: 13936, end: 13985 },
relative_location_in_cds: { start: 626, end: 675, length: 50 }
relative_location_in_cds: { start: 632, end: 681, length: 50 }
},
{
index: 9,
relative_location: { start: 15412, end: 15523 },
relative_location_in_cds: { start: 675, end: 786, length: 112 }
relative_location_in_cds: { start: 682, end: 793, length: 112 }
},
{
index: 10,
relative_location: { start: 16765, end: 17880 },
relative_location_in_cds: { start: 786, end: 1901, length: 1116 }
relative_location_in_cds: { start: 794, end: 1909, length: 1116 }
},
{
index: 11,
relative_location: { start: 20758, end: 25689 },
relative_location_in_cds: { start: 1901, end: 6832, length: 4932 }
relative_location_in_cds: { start: 1910, end: 6841, length: 4932 }
},
{
index: 12,
relative_location: { start: 29051, end: 29146 },
relative_location_in_cds: { start: 6832, end: 6927, length: 96 }
relative_location_in_cds: { start: 6842, end: 6937, length: 96 }
},
{
index: 13,
relative_location: { start: 31320, end: 31389 },
relative_location_in_cds: { start: 6927, end: 6996, length: 70 }
relative_location_in_cds: { start: 6938, end: 7007, length: 70 }
},
{
index: 14,
relative_location: { start: 39354, end: 39781 },
relative_location_in_cds: { start: 6996, end: 7423, length: 428 }
relative_location_in_cds: { start: 7008, end: 7435, length: 428 }
},
{
index: 15,
relative_location: { start: 40921, end: 41102 },
relative_location_in_cds: { start: 7423, end: 7604, length: 182 }
relative_location_in_cds: { start: 7436, end: 7617, length: 182 }
},
{
index: 16,
relative_location: { start: 42235, end: 42422 },
relative_location_in_cds: { start: 7604, end: 7791, length: 188 }
relative_location_in_cds: { start: 7618, end: 7805, length: 188 }
},
{
index: 17,
relative_location: { start: 47016, end: 47186 },
relative_location_in_cds: { start: 7791, end: 7961, length: 171 }
relative_location_in_cds: { start: 7806, end: 7976, length: 171 }
},
{
index: 18,
relative_location: { start: 47672, end: 48026 },
relative_location_in_cds: { start: 7961, end: 8315, length: 355 }
relative_location_in_cds: { start: 7977, end: 8331, length: 355 }
},
{
index: 19,
relative_location: { start: 54895, end: 55050 },
relative_location_in_cds: { start: 8315, end: 8470, length: 156 }
relative_location_in_cds: { start: 8332, end: 8487, length: 156 }
},
{
index: 20,
relative_location: { start: 55449, end: 55593 },
relative_location_in_cds: { start: 8470, end: 8614, length: 145 }
relative_location_in_cds: { start: 8488, end: 8632, length: 145 }
},
{
index: 21,
relative_location: { start: 59455, end: 59703 },
relative_location_in_cds: { start: 8614, end: 8732, length: 119 }
relative_location_in_cds: { start: 8633, end: 8751, length: 119 }
},
{
index: 22,
Expand Down
Loading

0 comments on commit 9451bb0

Please sign in to comment.