@teselagen/sequence-utils 0.1.22 → 0.1.23

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.
Files changed (135) hide show
  1. package/index.js +12030 -26126
  2. package/index.mjs +12119 -26124
  3. package/index.umd.js +24056 -38154
  4. package/package.json +2 -2
  5. package/src/DNAComplementMap.js +32 -0
  6. package/src/addGapsToSeqReads.js +417 -0
  7. package/src/addGapsToSeqReads.test.js +358 -0
  8. package/src/adjustAnnotationsToInsert.js +19 -0
  9. package/src/adjustBpsToReplaceOrInsert.js +50 -0
  10. package/src/adjustBpsToReplaceOrInsert.test.js +59 -0
  11. package/src/aliasedEnzymesByName.js +7363 -0
  12. package/src/aminoAcidToDegenerateDnaMap.js +32 -0
  13. package/src/aminoAcidToDegenerateRnaMap.js +32 -0
  14. package/src/aminoAcidToDnaRna.test.js +27 -0
  15. package/src/annotateSingleSeq.js +29 -0
  16. package/src/annotateSingleSeq.test.js +64 -0
  17. package/src/annotationTypes.js +23 -0
  18. package/src/autoAnnotate.js +242 -0
  19. package/src/autoAnnotate.test.js +1039 -0
  20. package/src/bioData.js +431 -0
  21. package/src/calculateNebTa.js +34 -0
  22. package/src/calculateNebTa.test.js +57 -0
  23. package/src/calculateNebTm.js +127 -0
  24. package/src/calculateNebTm.test.js +32 -0
  25. package/src/calculatePercentGC.js +3 -0
  26. package/src/calculatePercentGC.test.js +14 -0
  27. package/src/calculateTm.js +297 -0
  28. package/src/calculateTm.test.js +7 -0
  29. package/src/computeDigestFragments.js +179 -0
  30. package/src/computeDigestFragments.test.js +73 -0
  31. package/src/condensePairwiseAlignmentDifferences.js +85 -0
  32. package/src/condensePairwiseAlignmentDifferences.test.js +66 -0
  33. package/src/convertAACaretPositionOrRangeToDna.js +24 -0
  34. package/src/convertAACaretPositionOrRangeToDna.test.js +34 -0
  35. package/src/convertDnaCaretPositionOrRangeToAA.js +24 -0
  36. package/src/convertDnaCaretPositionOrRangeToAA.test.js +37 -0
  37. package/src/cutSequenceByRestrictionEnzyme.js +301 -0
  38. package/src/cutSequenceByRestrictionEnzyme.test.js +296 -0
  39. package/src/defaultEnzymesByName.js +278 -0
  40. package/src/degenerateDnaToAminoAcidMap.js +5 -0
  41. package/src/degenerateRnaToAminoAcidMap.js +5 -0
  42. package/src/deleteSequenceDataAtRange.js +5 -0
  43. package/src/deleteSequenceDataAtRange.test.js +146 -0
  44. package/src/diffUtils.js +64 -0
  45. package/src/diffUtils.test.js +74 -0
  46. package/src/doesEnzymeChopOutsideOfRecognitionSite.js +10 -0
  47. package/src/doesEnzymeChopOutsideOfRecognitionSite.test.js +41 -0
  48. package/src/featureTypesAndColors.js +152 -0
  49. package/src/featureTypesAndColors.test.js +52 -0
  50. package/src/filterAminoAcidSequenceString.js +13 -0
  51. package/src/filterAminoAcidSequenceString.test.js +22 -0
  52. package/src/filterSequenceString.js +22 -0
  53. package/src/filterSequenceString.test.js +13 -0
  54. package/src/findNearestRangeOfSequenceOverlapToPosition.js +39 -0
  55. package/src/findNearestRangeOfSequenceOverlapToPosition.test.js +31 -0
  56. package/src/findOrfsInPlasmid.js +26 -0
  57. package/src/findSequenceMatches.js +133 -0
  58. package/src/findSequenceMatches.test.js +286 -0
  59. package/src/generateAnnotations.js +34 -0
  60. package/src/generateSequenceData.js +206 -0
  61. package/src/generateSequenceData.test.js +22 -0
  62. package/src/getAllInsertionsInSeqReads.js +83 -0
  63. package/src/getAllInsertionsInSeqReads.test.js +26 -0
  64. package/src/getAminoAcidDataForEachBaseOfDna.js +163 -0
  65. package/src/getAminoAcidDataForEachBaseOfDna.test.js +424 -0
  66. package/src/getAminoAcidFromSequenceTriplet.js +22 -0
  67. package/src/getAminoAcidStringFromSequenceString.js +18 -0
  68. package/src/getAminoAcidStringFromSequenceString.test.js +18 -0
  69. package/src/getCodonRangeForAASliver.js +63 -0
  70. package/src/getComplementAminoAcidStringFromSequenceString.js +11 -0
  71. package/src/getComplementSequenceAndAnnotations.js +20 -0
  72. package/src/getComplementSequenceString.js +19 -0
  73. package/src/getComplementSequenceString.test.js +13 -0
  74. package/src/getCutsiteType.js +10 -0
  75. package/src/getCutsitesFromSequence.js +17 -0
  76. package/src/getDegenerateDnaStringFromAAString.js +8 -0
  77. package/src/getDegenerateRnaStringFromAAString.js +8 -0
  78. package/src/getDigestFragmentsForCutsites.js +105 -0
  79. package/src/getDigestFragmentsForRestrictionEnzymes.js +27 -0
  80. package/src/getDigestFragmentsForRestrictionEnzymes.test.js +228 -0
  81. package/src/getInsertBetweenVals.js +28 -0
  82. package/src/getInsertBetweenVals.test.js +33 -0
  83. package/src/getLeftAndRightOfSequenceInRangeGivenPosition.js +39 -0
  84. package/src/getLeftAndRightOfSequenceInRangeGivenPosition.test.js +80 -0
  85. package/src/getMassOfAaString.js +24 -0
  86. package/src/getMassofAaString.test.js +18 -0
  87. package/src/getOrfsFromSequence.js +124 -0
  88. package/src/getOrfsFromSequence.test.js +210 -0
  89. package/src/getOverlapBetweenTwoSequences.js +30 -0
  90. package/src/getOverlapBetweenTwoSequences.test.js +23 -0
  91. package/src/getPossiblePartsFromSequenceAndEnzymes.js +121 -0
  92. package/src/getPossiblePartsFromSequenceAndEnzymes.test.js +208 -0
  93. package/src/getReverseAminoAcidStringFromSequenceString.js +20 -0
  94. package/src/getReverseAminoAcidStringFromSequenceString.test.js +11 -0
  95. package/src/getReverseComplementAminoAcidStringFromSequenceString.js +7 -0
  96. package/src/getReverseComplementAnnotation.js +23 -0
  97. package/src/getReverseComplementAnnotation.test.js +44 -0
  98. package/src/getReverseComplementSequenceAndAnnotations.js +38 -0
  99. package/src/getReverseComplementSequenceAndAnnotations.test.js +105 -0
  100. package/src/getReverseComplementSequenceString.js +17 -0
  101. package/src/getReverseComplementSequenceString.test.js +11 -0
  102. package/src/getReverseSequenceString.js +12 -0
  103. package/src/getReverseSequenceString.test.js +9 -0
  104. package/src/getSequenceDataBetweenRange.js +131 -0
  105. package/src/getSequenceDataBetweenRange.test.js +474 -0
  106. package/src/getVirtualDigest.js +125 -0
  107. package/src/getVirtualDigest.test.js +134 -0
  108. package/src/guessIfSequenceIsDnaAndNotProtein.js +33 -0
  109. package/src/guessIfSequenceIsDnaAndNotProtein.test.js +34 -0
  110. package/src/index.js +106 -0
  111. package/src/index.test.js +38 -0
  112. package/src/insertGapsIntoRefSeq.js +38 -0
  113. package/src/insertGapsIntoRefSeq.test.js +20 -0
  114. package/src/insertSequenceDataAtPosition.js +2 -0
  115. package/src/insertSequenceDataAtPosition.test.js +75 -0
  116. package/src/insertSequenceDataAtPositionOrRange.js +249 -0
  117. package/src/insertSequenceDataAtPositionOrRange.test.js +547 -0
  118. package/src/isEnzymeType2S.js +3 -0
  119. package/src/mapAnnotationsToRows.js +174 -0
  120. package/src/mapAnnotationsToRows.test.js +425 -0
  121. package/src/prepareCircularViewData.js +17 -0
  122. package/src/prepareCircularViewData.test.js +196 -0
  123. package/src/prepareRowData.js +41 -0
  124. package/src/prepareRowData.test.js +36 -0
  125. package/src/prepareRowData_output1.json +391 -0
  126. package/src/proteinAlphabet.js +257 -0
  127. package/src/rotateBpsToPosition.js +13 -0
  128. package/src/rotateBpsToPosition.test.js +6 -0
  129. package/src/rotateSequenceDataToPosition.js +48 -0
  130. package/src/rotateSequenceDataToPosition.test.js +71 -0
  131. package/src/shiftAnnotationsByLen.js +17 -0
  132. package/src/threeLetterSequenceStringToAminoAcidMap.js +106 -0
  133. package/src/tidyUpAnnotation.js +182 -0
  134. package/src/tidyUpSequenceData.js +169 -0
  135. 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
+ }