@teselagen/sequence-utils 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +12030 -26126
- package/index.mjs +12119 -26124
- package/index.umd.js +24056 -38154
- package/package.json +4 -3
- package/src/DNAComplementMap.js +32 -0
- package/src/addGapsToSeqReads.js +417 -0
- package/src/addGapsToSeqReads.test.js +358 -0
- package/src/adjustAnnotationsToInsert.js +19 -0
- package/src/adjustBpsToReplaceOrInsert.js +50 -0
- package/src/adjustBpsToReplaceOrInsert.test.js +59 -0
- package/src/aliasedEnzymesByName.js +7363 -0
- package/src/aminoAcidToDegenerateDnaMap.js +32 -0
- package/src/aminoAcidToDegenerateRnaMap.js +32 -0
- package/src/aminoAcidToDnaRna.test.js +27 -0
- package/src/annotateSingleSeq.js +29 -0
- package/src/annotateSingleSeq.test.js +64 -0
- package/src/annotationTypes.js +23 -0
- package/src/autoAnnotate.js +242 -0
- package/src/autoAnnotate.test.js +1039 -0
- package/src/bioData.js +431 -0
- package/src/calculateNebTa.js +34 -0
- package/src/calculateNebTa.test.js +57 -0
- package/src/calculateNebTm.js +127 -0
- package/src/calculateNebTm.test.js +32 -0
- package/src/calculatePercentGC.js +3 -0
- package/src/calculatePercentGC.test.js +14 -0
- package/src/calculateTm.js +297 -0
- package/src/calculateTm.test.js +7 -0
- package/src/computeDigestFragments.js +179 -0
- package/src/computeDigestFragments.test.js +73 -0
- package/src/condensePairwiseAlignmentDifferences.js +85 -0
- package/src/condensePairwiseAlignmentDifferences.test.js +66 -0
- package/src/convertAACaretPositionOrRangeToDna.js +24 -0
- package/src/convertAACaretPositionOrRangeToDna.test.js +34 -0
- package/src/convertDnaCaretPositionOrRangeToAA.js +24 -0
- package/src/convertDnaCaretPositionOrRangeToAA.test.js +37 -0
- package/src/cutSequenceByRestrictionEnzyme.js +301 -0
- package/src/cutSequenceByRestrictionEnzyme.test.js +296 -0
- package/src/defaultEnzymesByName.js +278 -0
- package/src/degenerateDnaToAminoAcidMap.js +5 -0
- package/src/degenerateRnaToAminoAcidMap.js +5 -0
- package/src/deleteSequenceDataAtRange.js +5 -0
- package/src/deleteSequenceDataAtRange.test.js +146 -0
- package/src/diffUtils.js +64 -0
- package/src/diffUtils.test.js +74 -0
- package/src/doesEnzymeChopOutsideOfRecognitionSite.js +10 -0
- package/src/doesEnzymeChopOutsideOfRecognitionSite.test.js +41 -0
- package/src/featureTypesAndColors.js +152 -0
- package/src/featureTypesAndColors.test.js +52 -0
- package/src/filterAminoAcidSequenceString.js +13 -0
- package/src/filterAminoAcidSequenceString.test.js +22 -0
- package/src/filterSequenceString.js +22 -0
- package/src/filterSequenceString.test.js +13 -0
- package/src/findNearestRangeOfSequenceOverlapToPosition.js +39 -0
- package/src/findNearestRangeOfSequenceOverlapToPosition.test.js +31 -0
- package/src/findOrfsInPlasmid.js +26 -0
- package/src/findSequenceMatches.js +133 -0
- package/src/findSequenceMatches.test.js +286 -0
- package/src/generateAnnotations.js +34 -0
- package/src/generateSequenceData.js +206 -0
- package/src/generateSequenceData.test.js +22 -0
- package/src/getAllInsertionsInSeqReads.js +83 -0
- package/src/getAllInsertionsInSeqReads.test.js +26 -0
- package/src/getAminoAcidDataForEachBaseOfDna.js +163 -0
- package/src/getAminoAcidDataForEachBaseOfDna.test.js +424 -0
- package/src/getAminoAcidFromSequenceTriplet.js +22 -0
- package/src/getAminoAcidStringFromSequenceString.js +18 -0
- package/src/getAminoAcidStringFromSequenceString.test.js +18 -0
- package/src/getCodonRangeForAASliver.js +63 -0
- package/src/getComplementAminoAcidStringFromSequenceString.js +11 -0
- package/src/getComplementSequenceAndAnnotations.js +20 -0
- package/src/getComplementSequenceString.js +19 -0
- package/src/getComplementSequenceString.test.js +13 -0
- package/src/getCutsiteType.js +10 -0
- package/src/getCutsitesFromSequence.js +17 -0
- package/src/getDegenerateDnaStringFromAAString.js +8 -0
- package/src/getDegenerateRnaStringFromAAString.js +8 -0
- package/src/getDigestFragmentsForCutsites.js +105 -0
- package/src/getDigestFragmentsForRestrictionEnzymes.js +27 -0
- package/src/getDigestFragmentsForRestrictionEnzymes.test.js +228 -0
- package/src/getInsertBetweenVals.js +28 -0
- package/src/getInsertBetweenVals.test.js +33 -0
- package/src/getLeftAndRightOfSequenceInRangeGivenPosition.js +39 -0
- package/src/getLeftAndRightOfSequenceInRangeGivenPosition.test.js +80 -0
- package/src/getMassOfAaString.js +24 -0
- package/src/getMassofAaString.test.js +18 -0
- package/src/getOrfsFromSequence.js +124 -0
- package/src/getOrfsFromSequence.test.js +210 -0
- package/src/getOverlapBetweenTwoSequences.js +30 -0
- package/src/getOverlapBetweenTwoSequences.test.js +23 -0
- package/src/getPossiblePartsFromSequenceAndEnzymes.js +121 -0
- package/src/getPossiblePartsFromSequenceAndEnzymes.test.js +208 -0
- package/src/getReverseAminoAcidStringFromSequenceString.js +20 -0
- package/src/getReverseAminoAcidStringFromSequenceString.test.js +11 -0
- package/src/getReverseComplementAminoAcidStringFromSequenceString.js +7 -0
- package/src/getReverseComplementAnnotation.js +23 -0
- package/src/getReverseComplementAnnotation.test.js +44 -0
- package/src/getReverseComplementSequenceAndAnnotations.js +38 -0
- package/src/getReverseComplementSequenceAndAnnotations.test.js +105 -0
- package/src/getReverseComplementSequenceString.js +17 -0
- package/src/getReverseComplementSequenceString.test.js +11 -0
- package/src/getReverseSequenceString.js +12 -0
- package/src/getReverseSequenceString.test.js +9 -0
- package/src/getSequenceDataBetweenRange.js +131 -0
- package/src/getSequenceDataBetweenRange.test.js +474 -0
- package/src/getVirtualDigest.js +125 -0
- package/src/getVirtualDigest.test.js +134 -0
- package/src/guessIfSequenceIsDnaAndNotProtein.js +33 -0
- package/src/guessIfSequenceIsDnaAndNotProtein.test.js +34 -0
- package/src/index.js +106 -0
- package/src/index.test.js +38 -0
- package/src/insertGapsIntoRefSeq.js +38 -0
- package/src/insertGapsIntoRefSeq.test.js +20 -0
- package/src/insertSequenceDataAtPosition.js +2 -0
- package/src/insertSequenceDataAtPosition.test.js +75 -0
- package/src/insertSequenceDataAtPositionOrRange.js +249 -0
- package/src/insertSequenceDataAtPositionOrRange.test.js +547 -0
- package/src/isEnzymeType2S.js +3 -0
- package/src/mapAnnotationsToRows.js +174 -0
- package/src/mapAnnotationsToRows.test.js +425 -0
- package/src/prepareCircularViewData.js +17 -0
- package/src/prepareCircularViewData.test.js +196 -0
- package/src/prepareRowData.js +41 -0
- package/src/prepareRowData.test.js +36 -0
- package/src/prepareRowData_output1.json +391 -0
- package/src/proteinAlphabet.js +257 -0
- package/src/rotateBpsToPosition.js +13 -0
- package/src/rotateBpsToPosition.test.js +6 -0
- package/src/rotateSequenceDataToPosition.js +48 -0
- package/src/rotateSequenceDataToPosition.test.js +71 -0
- package/src/shiftAnnotationsByLen.js +17 -0
- package/src/threeLetterSequenceStringToAminoAcidMap.js +106 -0
- package/src/tidyUpAnnotation.js +182 -0
- package/src/tidyUpSequenceData.js +169 -0
- package/src/tidyUpSequenceData.test.js +332 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {each, forEach, startsWith, filter} from "lodash";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getYOffsetForPotentiallyCircularRange,
|
|
5
|
+
splitRangeIntoTwoPartsIfItIsCircular,
|
|
6
|
+
} from "@teselagen/range-utils";
|
|
7
|
+
|
|
8
|
+
export default function mapAnnotationsToRows(
|
|
9
|
+
annotations,
|
|
10
|
+
sequenceLength,
|
|
11
|
+
bpsPerRow,
|
|
12
|
+
{ splitForwardReverse } = {}
|
|
13
|
+
) {
|
|
14
|
+
const annotationsToRowsMap = {};
|
|
15
|
+
const yOffsetLevelMap = {};
|
|
16
|
+
const wrappedAnnotations = {};
|
|
17
|
+
each(annotations, annotation => {
|
|
18
|
+
const containsLocations = !!(
|
|
19
|
+
annotation.locations && annotation.locations.length
|
|
20
|
+
);
|
|
21
|
+
if (annotation.overlapsSelf) {
|
|
22
|
+
//if the annotation overlaps itself, first send a fake full spanning annotation thru the mapping function
|
|
23
|
+
if (!wrappedAnnotations[annotation.id]) {
|
|
24
|
+
mapAnnotationToRows({
|
|
25
|
+
wrappedAnnotations,
|
|
26
|
+
annotation: {
|
|
27
|
+
start: 0,
|
|
28
|
+
end: sequenceLength - 1,
|
|
29
|
+
id: `__tempAnnRemoveMe__${annotation.id}`
|
|
30
|
+
},
|
|
31
|
+
sequenceLength,
|
|
32
|
+
bpsPerRow,
|
|
33
|
+
annotationsToRowsMap,
|
|
34
|
+
yOffsetLevelMap,
|
|
35
|
+
containsLocations,
|
|
36
|
+
splitForwardReverse
|
|
37
|
+
});
|
|
38
|
+
wrappedAnnotations[annotation.id] = true;
|
|
39
|
+
// annotation.yOffset = wrappedAnnotations[annotation.id];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
mapAnnotationToRows({
|
|
44
|
+
wrappedAnnotations,
|
|
45
|
+
annotation,
|
|
46
|
+
sequenceLength,
|
|
47
|
+
bpsPerRow,
|
|
48
|
+
annotationsToRowsMap,
|
|
49
|
+
yOffsetLevelMap,
|
|
50
|
+
containsLocations,
|
|
51
|
+
splitForwardReverse
|
|
52
|
+
});
|
|
53
|
+
if (containsLocations) {
|
|
54
|
+
annotation.locations.forEach(location => {
|
|
55
|
+
mapAnnotationToRows({
|
|
56
|
+
wrappedAnnotations,
|
|
57
|
+
annotation,
|
|
58
|
+
sequenceLength,
|
|
59
|
+
bpsPerRow,
|
|
60
|
+
annotationsToRowsMap,
|
|
61
|
+
yOffsetLevelMap,
|
|
62
|
+
location,
|
|
63
|
+
splitForwardReverse
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
forEach(annotationsToRowsMap, (annotationsForRow, i) => {
|
|
69
|
+
annotationsToRowsMap[i] = filter(
|
|
70
|
+
annotationsForRow,
|
|
71
|
+
ann => !startsWith(ann.id, "__tempAnnRemoveMe__")
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
return annotationsToRowsMap;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function mapAnnotationToRows({
|
|
78
|
+
annotation,
|
|
79
|
+
sequenceLength,
|
|
80
|
+
bpsPerRow,
|
|
81
|
+
annotationsToRowsMap,
|
|
82
|
+
yOffsetLevelMap,
|
|
83
|
+
location,
|
|
84
|
+
containsLocations,
|
|
85
|
+
splitForwardReverse
|
|
86
|
+
}) {
|
|
87
|
+
const ranges = splitRangeIntoTwoPartsIfItIsCircular(
|
|
88
|
+
location || annotation,
|
|
89
|
+
sequenceLength
|
|
90
|
+
);
|
|
91
|
+
ranges.forEach((range, index) => {
|
|
92
|
+
// if (!isPositiveInteger(range.start)) {}
|
|
93
|
+
const startingRow = Math.floor(range.start / bpsPerRow);
|
|
94
|
+
const endingRow = Math.floor(range.end / bpsPerRow);
|
|
95
|
+
// const numberOfRows = endingRow - startingRow + 1;
|
|
96
|
+
for (let rowNumber = startingRow; rowNumber <= endingRow; rowNumber++) {
|
|
97
|
+
if (!annotationsToRowsMap[rowNumber]) {
|
|
98
|
+
annotationsToRowsMap[rowNumber] = [];
|
|
99
|
+
}
|
|
100
|
+
const key = splitForwardReverse
|
|
101
|
+
? annotation.forward
|
|
102
|
+
? rowNumber + "_forward"
|
|
103
|
+
: rowNumber + "_reverse"
|
|
104
|
+
: rowNumber;
|
|
105
|
+
|
|
106
|
+
const annotationsForRow = annotationsToRowsMap[rowNumber];
|
|
107
|
+
if (!yOffsetLevelMap[key]) {
|
|
108
|
+
yOffsetLevelMap[key] = [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let yOffset;
|
|
112
|
+
const yOffsetsForRow = yOffsetLevelMap[key];
|
|
113
|
+
const start =
|
|
114
|
+
rowNumber === startingRow ? range.start : rowNumber * bpsPerRow;
|
|
115
|
+
const end =
|
|
116
|
+
rowNumber === endingRow
|
|
117
|
+
? range.end
|
|
118
|
+
: rowNumber * bpsPerRow + bpsPerRow - 1;
|
|
119
|
+
if (annotation.overlapsSelf) {
|
|
120
|
+
annotationsForRow.forEach(ann => {
|
|
121
|
+
if (ann.id === `__tempAnnRemoveMe__${annotation.id}`) {
|
|
122
|
+
yOffset = ann.yOffset;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
if (location) {
|
|
127
|
+
//if there's a location then we will just use the previous yOffset for its parent annotation
|
|
128
|
+
annotationsForRow.forEach(ann => {
|
|
129
|
+
if (ann.id === annotation.id) {
|
|
130
|
+
yOffset = ann.yOffset;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
//we need to pass both ranges into this function so that we can correctly
|
|
135
|
+
//get the y-offset for circular features that start and end on the same row
|
|
136
|
+
//we pass the entire annotation range here and compare it only with ranges that have already been added to the row
|
|
137
|
+
if (
|
|
138
|
+
index > 0 && //second half of an annotation range
|
|
139
|
+
annotationsForRow.length && //there are already annotations within the row
|
|
140
|
+
annotationsForRow[annotationsForRow.length - 1].annotation ===
|
|
141
|
+
annotation
|
|
142
|
+
) {
|
|
143
|
+
//the first chunk of the annotation has already been pushed into the row,
|
|
144
|
+
//so set the yOffset for the range chunk to the already calculated yOffset
|
|
145
|
+
yOffset = annotationsForRow[annotationsForRow.length - 1].yOffset;
|
|
146
|
+
} else {
|
|
147
|
+
yOffset = getYOffsetForPotentiallyCircularRange(
|
|
148
|
+
annotation,
|
|
149
|
+
yOffsetsForRow
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
//add the new yOffset to the yOffset array
|
|
153
|
+
if (!yOffsetsForRow[yOffset]) yOffsetsForRow[yOffset] = [];
|
|
154
|
+
yOffsetsForRow[yOffset].push({
|
|
155
|
+
start: start,
|
|
156
|
+
end: end
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
annotationsForRow.push({
|
|
162
|
+
id: annotation.id,
|
|
163
|
+
annotation: annotation,
|
|
164
|
+
start: start,
|
|
165
|
+
end: end,
|
|
166
|
+
...(containsLocations && { containsLocations }),
|
|
167
|
+
...(location && { isJoinedLocation: !!location }),
|
|
168
|
+
yOffset: yOffset,
|
|
169
|
+
enclosingRangeType: range.type //either "beginning", "end" or "beginningAndEnd"
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
// return annotationsToRowsMap;
|
|
174
|
+
}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import chai from "chai";
|
|
4
|
+
|
|
5
|
+
import {expect} from "chai";
|
|
6
|
+
import chaiSubset from "chai-subset";
|
|
7
|
+
import mapAnnotationsToRows from "./mapAnnotationsToRows.js";
|
|
8
|
+
chai.use(chaiSubset);
|
|
9
|
+
describe("mapAnnotationsToRows", () => {
|
|
10
|
+
it("maps overlapsSelf=true annotations to the same y-offset correctly", () => {
|
|
11
|
+
const annotation1 = {
|
|
12
|
+
start: 1,
|
|
13
|
+
end: 3,
|
|
14
|
+
id: "a"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const annotationWraps = {
|
|
18
|
+
start: 1,
|
|
19
|
+
end: 3,
|
|
20
|
+
overlapsSelf: true,
|
|
21
|
+
id: "overlaps self"
|
|
22
|
+
};
|
|
23
|
+
const annotationWrapsAddon = {
|
|
24
|
+
start: 4,
|
|
25
|
+
end: 0,
|
|
26
|
+
overlapsSelf: true,
|
|
27
|
+
isWrappedAddon: true,
|
|
28
|
+
id: "overlaps self"
|
|
29
|
+
};
|
|
30
|
+
const annotation3 = {
|
|
31
|
+
start: 0,
|
|
32
|
+
end: 0,
|
|
33
|
+
id: "b"
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const annotation4 = {
|
|
37
|
+
start: 0,
|
|
38
|
+
end: 0,
|
|
39
|
+
id: "c"
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const annotations = [
|
|
43
|
+
annotation1,
|
|
44
|
+
annotationWraps,
|
|
45
|
+
annotationWrapsAddon,
|
|
46
|
+
annotation3,
|
|
47
|
+
annotation4
|
|
48
|
+
];
|
|
49
|
+
const sequenceLength = 10;
|
|
50
|
+
const bpsPerRow = 5;
|
|
51
|
+
const annotationsToRowsMap = mapAnnotationsToRows(
|
|
52
|
+
annotations,
|
|
53
|
+
sequenceLength,
|
|
54
|
+
bpsPerRow
|
|
55
|
+
);
|
|
56
|
+
expect(annotationsToRowsMap[0]).to.containSubset([
|
|
57
|
+
{
|
|
58
|
+
annotation: annotationWraps,
|
|
59
|
+
start: 1,
|
|
60
|
+
end: 3,
|
|
61
|
+
id: annotationWraps.id,
|
|
62
|
+
yOffset: 1,
|
|
63
|
+
enclosingRangeType: "beginningAndEnd"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
annotation: annotationWrapsAddon,
|
|
67
|
+
start: 0,
|
|
68
|
+
end: 0,
|
|
69
|
+
id: annotationWrapsAddon.id,
|
|
70
|
+
yOffset: 1,
|
|
71
|
+
enclosingRangeType: "end"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
annotation: annotationWrapsAddon,
|
|
75
|
+
start: 4,
|
|
76
|
+
end: 4,
|
|
77
|
+
id: annotationWrapsAddon.id,
|
|
78
|
+
yOffset: 1,
|
|
79
|
+
enclosingRangeType: "beginning"
|
|
80
|
+
}
|
|
81
|
+
]);
|
|
82
|
+
});
|
|
83
|
+
it("maps overlapping annotations with joined locations to rows correctly", () => {
|
|
84
|
+
const annotation1 = {
|
|
85
|
+
start: 0,
|
|
86
|
+
end: 9,
|
|
87
|
+
locations: [
|
|
88
|
+
{ start: 0, end: 1 },
|
|
89
|
+
{ start: 7, end: 9 }
|
|
90
|
+
],
|
|
91
|
+
id: "a"
|
|
92
|
+
};
|
|
93
|
+
const annotation2 = {
|
|
94
|
+
start: 0,
|
|
95
|
+
end: 9,
|
|
96
|
+
locations: [
|
|
97
|
+
{ start: 0, end: 1 },
|
|
98
|
+
{ start: 2, end: 4 },
|
|
99
|
+
{ start: 9, end: 9 }
|
|
100
|
+
],
|
|
101
|
+
id: "b"
|
|
102
|
+
};
|
|
103
|
+
const annotations = [annotation1, annotation2];
|
|
104
|
+
const sequenceLength = 10;
|
|
105
|
+
const bpsPerRow = 5;
|
|
106
|
+
const annotationsToRowsMap = mapAnnotationsToRows(
|
|
107
|
+
annotations,
|
|
108
|
+
sequenceLength,
|
|
109
|
+
bpsPerRow
|
|
110
|
+
);
|
|
111
|
+
expect(annotationsToRowsMap[0]).to.containSubset([
|
|
112
|
+
{
|
|
113
|
+
annotation: annotation1,
|
|
114
|
+
start: 0,
|
|
115
|
+
end: 4,
|
|
116
|
+
id: annotation1.id,
|
|
117
|
+
yOffset: 0,
|
|
118
|
+
containsLocations: true,
|
|
119
|
+
enclosingRangeType: "beginningAndEnd"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
annotation: annotation1,
|
|
123
|
+
start: 0,
|
|
124
|
+
end: 1,
|
|
125
|
+
id: annotation1.id,
|
|
126
|
+
yOffset: 0,
|
|
127
|
+
isJoinedLocation: true,
|
|
128
|
+
enclosingRangeType: "beginningAndEnd"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
start: 0,
|
|
132
|
+
end: 4,
|
|
133
|
+
id: annotation2.id,
|
|
134
|
+
yOffset: 1,
|
|
135
|
+
annotation: annotation2,
|
|
136
|
+
enclosingRangeType: "beginningAndEnd"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
annotation: annotation2,
|
|
140
|
+
start: 0,
|
|
141
|
+
end: 1,
|
|
142
|
+
id: annotation2.id,
|
|
143
|
+
yOffset: 1,
|
|
144
|
+
isJoinedLocation: true,
|
|
145
|
+
enclosingRangeType: "beginningAndEnd"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
annotation: annotation2,
|
|
149
|
+
start: 2,
|
|
150
|
+
end: 4,
|
|
151
|
+
id: annotation2.id,
|
|
152
|
+
yOffset: 1,
|
|
153
|
+
isJoinedLocation: true,
|
|
154
|
+
enclosingRangeType: "beginningAndEnd"
|
|
155
|
+
}
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
expect(annotationsToRowsMap[1]).to.containSubset([
|
|
159
|
+
{
|
|
160
|
+
annotation: annotation1,
|
|
161
|
+
start: 5,
|
|
162
|
+
end: 9,
|
|
163
|
+
id: annotation1.id,
|
|
164
|
+
yOffset: 0,
|
|
165
|
+
containsLocations: true,
|
|
166
|
+
enclosingRangeType: "beginningAndEnd"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
annotation: annotation1,
|
|
170
|
+
start: 7,
|
|
171
|
+
end: 9,
|
|
172
|
+
id: annotation1.id,
|
|
173
|
+
yOffset: 0,
|
|
174
|
+
isJoinedLocation: true,
|
|
175
|
+
enclosingRangeType: "beginningAndEnd"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
start: 5,
|
|
179
|
+
end: 9,
|
|
180
|
+
id: annotation2.id,
|
|
181
|
+
yOffset: 1,
|
|
182
|
+
annotation: annotation2,
|
|
183
|
+
enclosingRangeType: "beginningAndEnd"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
annotation: annotation2,
|
|
187
|
+
start: 9,
|
|
188
|
+
end: 9,
|
|
189
|
+
id: annotation2.id,
|
|
190
|
+
yOffset: 1,
|
|
191
|
+
isJoinedLocation: true,
|
|
192
|
+
enclosingRangeType: "beginningAndEnd"
|
|
193
|
+
}
|
|
194
|
+
]);
|
|
195
|
+
});
|
|
196
|
+
it("maps overlapping annotations to rows correctly", () => {
|
|
197
|
+
const annotation1 = {
|
|
198
|
+
start: 0,
|
|
199
|
+
end: 9,
|
|
200
|
+
id: "a"
|
|
201
|
+
};
|
|
202
|
+
const annotation2 = {
|
|
203
|
+
start: 0,
|
|
204
|
+
end: 9,
|
|
205
|
+
id: "b"
|
|
206
|
+
};
|
|
207
|
+
const annotations = [annotation1, annotation2];
|
|
208
|
+
const sequenceLength = 10;
|
|
209
|
+
const bpsPerRow = 5;
|
|
210
|
+
const annotationsToRowsMap = mapAnnotationsToRows(
|
|
211
|
+
annotations,
|
|
212
|
+
sequenceLength,
|
|
213
|
+
bpsPerRow
|
|
214
|
+
);
|
|
215
|
+
expect(annotationsToRowsMap).to.deep.equal({
|
|
216
|
+
0: [
|
|
217
|
+
{
|
|
218
|
+
annotation: annotation1,
|
|
219
|
+
start: 0,
|
|
220
|
+
end: 4,
|
|
221
|
+
id: annotation1.id,
|
|
222
|
+
yOffset: 0,
|
|
223
|
+
enclosingRangeType: "beginningAndEnd"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
start: 0,
|
|
227
|
+
end: 4,
|
|
228
|
+
id: annotation2.id,
|
|
229
|
+
yOffset: 1,
|
|
230
|
+
annotation: annotation2,
|
|
231
|
+
enclosingRangeType: "beginningAndEnd"
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
1: [
|
|
235
|
+
{
|
|
236
|
+
annotation: annotation1,
|
|
237
|
+
start: 5,
|
|
238
|
+
end: 9,
|
|
239
|
+
id: annotation1.id,
|
|
240
|
+
yOffset: 0,
|
|
241
|
+
enclosingRangeType: "beginningAndEnd"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
start: 5,
|
|
245
|
+
end: 9,
|
|
246
|
+
id: annotation2.id,
|
|
247
|
+
yOffset: 1,
|
|
248
|
+
annotation: annotation2,
|
|
249
|
+
enclosingRangeType: "beginningAndEnd"
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
it("correctly calculates y-offset for annotation split by origin", () => {
|
|
255
|
+
const annotation1 = {
|
|
256
|
+
start: 7,
|
|
257
|
+
end: 9,
|
|
258
|
+
id: "a"
|
|
259
|
+
};
|
|
260
|
+
const annotation2 = {
|
|
261
|
+
start: 5,
|
|
262
|
+
end: 3,
|
|
263
|
+
id: "b"
|
|
264
|
+
};
|
|
265
|
+
const annotations = [annotation1, annotation2];
|
|
266
|
+
const sequenceLength = 10;
|
|
267
|
+
const bpsPerRow = 10;
|
|
268
|
+
const annotationsToRowsMap = mapAnnotationsToRows(
|
|
269
|
+
annotations,
|
|
270
|
+
sequenceLength,
|
|
271
|
+
bpsPerRow
|
|
272
|
+
);
|
|
273
|
+
expect(annotationsToRowsMap).to.deep.equal({
|
|
274
|
+
0: [
|
|
275
|
+
{
|
|
276
|
+
start: 7,
|
|
277
|
+
end: 9,
|
|
278
|
+
id: annotation1.id,
|
|
279
|
+
yOffset: 0,
|
|
280
|
+
annotation: annotation1,
|
|
281
|
+
enclosingRangeType: "beginningAndEnd"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
annotation: annotation2,
|
|
285
|
+
start: 0,
|
|
286
|
+
end: 3,
|
|
287
|
+
id: annotation2.id,
|
|
288
|
+
yOffset: 1,
|
|
289
|
+
enclosingRangeType: "end"
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
annotation: annotation2,
|
|
293
|
+
start: 5,
|
|
294
|
+
end: 9,
|
|
295
|
+
id: annotation2.id,
|
|
296
|
+
yOffset: 1,
|
|
297
|
+
enclosingRangeType: "beginning"
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
it("correctly calculates y-offset for annotation split by origin (different ordering of annotations)", () => {
|
|
303
|
+
const annotation1 = {
|
|
304
|
+
start: 5,
|
|
305
|
+
end: 3,
|
|
306
|
+
id: "a"
|
|
307
|
+
};
|
|
308
|
+
const annotation2 = {
|
|
309
|
+
start: 7,
|
|
310
|
+
end: 9,
|
|
311
|
+
id: "b"
|
|
312
|
+
};
|
|
313
|
+
const annotations = [annotation1, annotation2];
|
|
314
|
+
const sequenceLength = 10;
|
|
315
|
+
const bpsPerRow = 10;
|
|
316
|
+
const annotationsToRowsMap = mapAnnotationsToRows(
|
|
317
|
+
annotations,
|
|
318
|
+
sequenceLength,
|
|
319
|
+
bpsPerRow
|
|
320
|
+
);
|
|
321
|
+
expect(annotationsToRowsMap).to.deep.equal({
|
|
322
|
+
0: [
|
|
323
|
+
{
|
|
324
|
+
annotation: annotation1,
|
|
325
|
+
start: 0,
|
|
326
|
+
end: 3,
|
|
327
|
+
id: annotation1.id,
|
|
328
|
+
yOffset: 0,
|
|
329
|
+
enclosingRangeType: "end"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
annotation: annotation1,
|
|
333
|
+
start: 5,
|
|
334
|
+
end: 9,
|
|
335
|
+
id: annotation1.id,
|
|
336
|
+
yOffset: 0,
|
|
337
|
+
enclosingRangeType: "beginning"
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
start: 7,
|
|
341
|
+
end: 9,
|
|
342
|
+
id: annotation2.id,
|
|
343
|
+
yOffset: 1,
|
|
344
|
+
annotation: annotation2,
|
|
345
|
+
enclosingRangeType: "beginningAndEnd"
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("maps single annotation to rows correctly", () => {
|
|
352
|
+
const annotation1 = {
|
|
353
|
+
start: 0,
|
|
354
|
+
end: 9,
|
|
355
|
+
id: "a"
|
|
356
|
+
};
|
|
357
|
+
const annotations = [annotation1];
|
|
358
|
+
const sequenceLength = 10;
|
|
359
|
+
const bpsPerRow = 5;
|
|
360
|
+
const annotationsToRowsMap = mapAnnotationsToRows(
|
|
361
|
+
annotations,
|
|
362
|
+
sequenceLength,
|
|
363
|
+
bpsPerRow
|
|
364
|
+
);
|
|
365
|
+
expect(annotationsToRowsMap).to.deep.equal({
|
|
366
|
+
0: [
|
|
367
|
+
{
|
|
368
|
+
annotation: annotation1,
|
|
369
|
+
start: 0,
|
|
370
|
+
end: 4,
|
|
371
|
+
id: annotation1.id,
|
|
372
|
+
yOffset: 0,
|
|
373
|
+
enclosingRangeType: "beginningAndEnd"
|
|
374
|
+
}
|
|
375
|
+
],
|
|
376
|
+
1: [
|
|
377
|
+
{
|
|
378
|
+
annotation: annotation1,
|
|
379
|
+
start: 5,
|
|
380
|
+
end: 9,
|
|
381
|
+
id: annotation1.id,
|
|
382
|
+
yOffset: 0,
|
|
383
|
+
enclosingRangeType: "beginningAndEnd"
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
it("maps annotations to rows correctly when the annotations are passed as an object", () => {
|
|
389
|
+
const annotation1 = {
|
|
390
|
+
start: 0,
|
|
391
|
+
end: 9,
|
|
392
|
+
id: "a"
|
|
393
|
+
};
|
|
394
|
+
const annotations = { a: annotation1 };
|
|
395
|
+
const sequenceLength = 10;
|
|
396
|
+
const bpsPerRow = 5;
|
|
397
|
+
const annotationsToRowsMap = mapAnnotationsToRows(
|
|
398
|
+
annotations,
|
|
399
|
+
sequenceLength,
|
|
400
|
+
bpsPerRow
|
|
401
|
+
);
|
|
402
|
+
expect(annotationsToRowsMap).to.deep.equal({
|
|
403
|
+
0: [
|
|
404
|
+
{
|
|
405
|
+
annotation: annotation1,
|
|
406
|
+
start: 0,
|
|
407
|
+
end: 4,
|
|
408
|
+
id: annotation1.id,
|
|
409
|
+
yOffset: 0,
|
|
410
|
+
enclosingRangeType: "beginningAndEnd"
|
|
411
|
+
}
|
|
412
|
+
],
|
|
413
|
+
1: [
|
|
414
|
+
{
|
|
415
|
+
annotation: annotation1,
|
|
416
|
+
start: 5,
|
|
417
|
+
end: 9,
|
|
418
|
+
id: annotation1.id,
|
|
419
|
+
yOffset: 0,
|
|
420
|
+
enclosingRangeType: "beginningAndEnd"
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { cloneDeep } from 'lodash';
|
|
2
|
+
import { getYOffsetsForPotentiallyCircularRanges } from '@teselagen/range-utils';
|
|
3
|
+
import { annotationTypes } from './annotationTypes';
|
|
4
|
+
|
|
5
|
+
//basically just adds yOffsets to the annotations
|
|
6
|
+
export default function prepareCircularViewData(sequenceData) {
|
|
7
|
+
const clonedSeqData = cloneDeep(sequenceData);
|
|
8
|
+
annotationTypes.forEach((annotationType) => {
|
|
9
|
+
if (annotationType !== 'cutsites') {
|
|
10
|
+
const maxYOffset = getYOffsetsForPotentiallyCircularRanges(
|
|
11
|
+
clonedSeqData[annotationType]
|
|
12
|
+
).maxYOffset;
|
|
13
|
+
clonedSeqData[annotationType].maxYOffset = maxYOffset;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return clonedSeqData;
|
|
17
|
+
}
|