@jbrowse/plugin-alignments 1.6.5 → 1.6.8

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 (52) hide show
  1. package/dist/AlignmentsFeatureDetail/index.d.ts +1 -1
  2. package/dist/BamAdapter/BamSlightlyLazyFeature.d.ts +2 -10
  3. package/dist/BamAdapter/MismatchParser.d.ts +3 -5
  4. package/dist/BamAdapter/configSchema.d.ts +1 -1
  5. package/dist/CramAdapter/CramSlightlyLazyFeature.d.ts +1 -2
  6. package/dist/CramAdapter/configSchema.d.ts +1 -1
  7. package/dist/HtsgetBamAdapter/configSchema.d.ts +1 -1
  8. package/dist/LinearAlignmentsDisplay/models/configSchema.d.ts +1 -1
  9. package/dist/LinearAlignmentsDisplay/models/model.d.ts +1 -1
  10. package/dist/LinearPileupDisplay/configSchema.d.ts +1 -1
  11. package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
  12. package/dist/LinearSNPCoverageDisplay/models/configSchema.d.ts +1 -1
  13. package/dist/LinearSNPCoverageDisplay/models/model.d.ts +2 -2
  14. package/dist/PileupRenderer/PileupRenderer.d.ts +20 -6
  15. package/dist/PileupRenderer/configSchema.d.ts +1 -1
  16. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +3 -11
  17. package/dist/SNPCoverageAdapter/configSchema.d.ts +1 -1
  18. package/dist/SNPCoverageRenderer/SNPCoverageRenderer.d.ts +1 -1
  19. package/dist/SNPCoverageRenderer/configSchema.d.ts +1 -1
  20. package/dist/SNPCoverageRenderer/index.d.ts +1 -1
  21. package/dist/plugin-alignments.cjs.development.js +795 -682
  22. package/dist/plugin-alignments.cjs.development.js.map +1 -1
  23. package/dist/plugin-alignments.cjs.production.min.js +1 -1
  24. package/dist/plugin-alignments.cjs.production.min.js.map +1 -1
  25. package/dist/plugin-alignments.esm.js +797 -684
  26. package/dist/plugin-alignments.esm.js.map +1 -1
  27. package/dist/util.d.ts +4 -0
  28. package/package.json +4 -4
  29. package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +23 -14
  30. package/src/BamAdapter/BamAdapter.ts +10 -7
  31. package/src/BamAdapter/BamSlightlyLazyFeature.ts +11 -75
  32. package/src/BamAdapter/MismatchParser.test.ts +53 -297
  33. package/src/BamAdapter/MismatchParser.ts +54 -116
  34. package/src/BamAdapter/configSchema.ts +0 -4
  35. package/src/CramAdapter/CramSlightlyLazyFeature.ts +3 -10
  36. package/src/LinearAlignmentsDisplay/components/AlignmentsDisplay.tsx +38 -30
  37. package/src/LinearAlignmentsDisplay/models/model.tsx +10 -9
  38. package/src/LinearPileupDisplay/components/ColorByModifications.tsx +76 -80
  39. package/src/LinearPileupDisplay/components/ColorByTag.tsx +24 -23
  40. package/src/LinearPileupDisplay/components/FilterByTag.tsx +73 -68
  41. package/src/LinearPileupDisplay/components/SetFeatureHeight.tsx +28 -26
  42. package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +24 -13
  43. package/src/LinearPileupDisplay/components/SortByTag.tsx +29 -21
  44. package/src/LinearPileupDisplay/model.ts +12 -6
  45. package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +5 -3
  46. package/src/LinearSNPCoverageDisplay/models/configSchema.ts +4 -5
  47. package/src/PileupRenderer/PileupRenderer.tsx +202 -70
  48. package/src/PileupRenderer/components/PileupRendering.tsx +5 -3
  49. package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +192 -237
  50. package/src/SNPCoverageRenderer/SNPCoverageRenderer.ts +91 -60
  51. package/src/SNPCoverageRenderer/configSchema.js +1 -1
  52. package/src/util.ts +25 -0
@@ -1,8 +1,7 @@
1
1
  import { createJBrowseTheme } from '@jbrowse/core/ui'
2
- import { featureSpanPx } from '@jbrowse/core/util'
2
+ import { featureSpanPx, bpSpanPx } from '@jbrowse/core/util'
3
3
  import { Feature } from '@jbrowse/core/util/simpleFeature'
4
4
  import { readConfObject } from '@jbrowse/core/configuration'
5
- import { bpSpanPx } from '@jbrowse/core/util'
6
5
  import { RenderArgsDeserialized as FeatureRenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType'
7
6
  import {
8
7
  getOrigin,
@@ -24,7 +23,7 @@ export interface RenderArgsDeserializedWithFeatures
24
23
  features: Map<string, Feature>
25
24
  ticks: { values: number[] }
26
25
  displayCrossHatches: boolean
27
- modificationTagMap: Record<string, string>
26
+ modificationTagMap?: Record<string, string>
28
27
  }
29
28
 
30
29
  type Counts = {
@@ -49,12 +48,12 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
49
48
  regions,
50
49
  bpPerPx,
51
50
  displayCrossHatches,
52
- modificationTagMap,
51
+ modificationTagMap = {},
53
52
  scaleOpts,
54
53
  height: unadjustedHeight,
55
54
  theme: configTheme,
56
55
  config: cfg,
57
- ticks: { values },
56
+ ticks,
58
57
  } = props
59
58
  const theme = createJBrowseTheme(configTheme)
60
59
  const [region] = regions
@@ -66,11 +65,20 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
66
65
  const offset = YSCALEBAR_LABEL_OFFSET
67
66
  const height = unadjustedHeight - offset * 2
68
67
 
68
+ const { domain } = scaleOpts
69
+ if (!domain) {
70
+ return
71
+ }
69
72
  const opts = { ...scaleOpts, range: [0, height] }
70
73
  const viewScale = getScale(opts)
71
- const snpViewScale = getScale({ ...opts, scaleType: 'linear' })
74
+
75
+ // clipping and insertion indicators, uses a smaller height/2 scale
76
+ const indicatorViewScale = getScale({
77
+ ...opts,
78
+ range: [0, height / 2],
79
+ scaleType: 'linear',
80
+ })
72
81
  const originY = getOrigin(scaleOpts.scaleType)
73
- const snpOriginY = getOrigin('linear')
74
82
 
75
83
  const indicatorThreshold = readConfObject(cfg, 'indicatorThreshold')
76
84
  const drawInterbaseCounts = readConfObject(cfg, 'drawInterbaseCounts')
@@ -81,9 +89,10 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
81
89
  const toY = (n: number) => height - (viewScale(n) || 0) + offset
82
90
  const toHeight = (n: number) => toY(originY) - toY(n)
83
91
 
84
- // this is always linear scale, even when plotted on top of log scale
85
- const snpToY = (n: number) => height - (snpViewScale(n) || 0) + offset
86
- const snpToHeight = (n: number) => snpToY(snpOriginY) - snpToY(n)
92
+ const indicatorToY = (n: number) =>
93
+ height - (indicatorViewScale(n) || 0) + offset
94
+ const indicatorToHeight = (n: number) =>
95
+ indicatorToY(getOrigin('linear')) - indicatorToY(n)
87
96
 
88
97
  const colorForBase: { [key: string]: string } = {
89
98
  A: theme.palette.bases.A.main,
@@ -106,91 +115,113 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
106
115
  // Use two pass rendering, which helps in visualizing the SNPs at higher
107
116
  // bpPerPx First pass: draw the gray background
108
117
  ctx.fillStyle = colorForBase.total
109
- coverage.forEach(feature => {
118
+ for (let i = 0; i < coverage.length; i++) {
119
+ const feature = coverage[i]
110
120
  const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx)
111
121
  const w = rightPx - leftPx + 0.3
112
122
  const score = feature.get('score') as number
113
123
  ctx.fillRect(leftPx, toY(score), w, toHeight(score))
114
- })
115
- ctx.fillStyle = 'grey'
116
- ctx.beginPath()
117
- ctx.lineTo(0, 0)
118
- ctx.moveTo(0, width)
119
- ctx.stroke()
124
+ }
125
+
126
+ // Keep track of previous total which we will use it to draw the interbase
127
+ // indicator (if there is a sudden clip, there will be no read coverage but
128
+ // there will be "clip" coverage) at that position beyond the read. if the
129
+ // clip is right at a block boundary then prevTotal will not be available,
130
+ // so this is a best attempt to plot interbase indicator at the "cliffs"
131
+ let prevTotal = 0
132
+
133
+ // extraHorizontallyFlippedOffset is used to draw interbase items, which
134
+ // are located to the left when forward and right when reversed
135
+ const extraHorizontallyFlippedOffset = region.reversed ? 1 / bpPerPx : 0
120
136
 
121
137
  // Second pass: draw the SNP data, and add a minimum feature width of 1px
122
138
  // which can be wider than the actual bpPerPx This reduces overdrawing of
123
139
  // the grey background over the SNPs
124
- coverage.forEach(feature => {
140
+
141
+ for (let i = 0; i < coverage.length; i++) {
142
+ const feature = coverage[i]
125
143
  const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx)
126
144
 
145
+ const score = feature.get('score') as number
127
146
  const snpinfo = feature.get('snpinfo') as SNPInfo
128
147
  const w = Math.max(rightPx - leftPx + 0.3, 1)
129
148
  const totalScore = snpinfo.total
149
+ const keys = Object.keys(snpinfo.cov).sort()
130
150
 
131
- Object.entries(snpinfo.cov)
132
- .sort(([a], [b]) => {
133
- if (a < b) {
134
- return -1
135
- }
136
- if (a > b) {
137
- return 1
138
- }
139
- return 0
140
- })
141
- .reduce((curr, [base, { total }]) => {
142
- ctx.fillStyle =
143
- colorForBase[base] ||
144
- modificationTagMap[base.replace('mod_', '')] ||
145
- 'red'
146
- ctx.fillRect(leftPx, snpToY(total + curr), w, snpToHeight(total))
147
- return curr + total
148
- }, 0)
149
-
150
- const interbaseEvents = Object.entries(snpinfo.noncov)
151
+ let curr = 0
152
+ for (let i = 0; i < keys.length; i++) {
153
+ const base = keys[i]
154
+ const { total } = snpinfo.cov[base]
155
+ ctx.fillStyle =
156
+ colorForBase[base] ||
157
+ modificationTagMap[base.replace('mod_', '')] ||
158
+ '#888'
159
+
160
+ const height = toHeight(score)
161
+ const bottom = toY(score) + height
162
+ ctx.fillRect(
163
+ leftPx,
164
+ bottom - ((total + curr) / score) * height,
165
+ w,
166
+ (total / score) * height,
167
+ )
168
+ curr += total
169
+ }
170
+
171
+ const interbaseEvents = Object.keys(snpinfo.noncov)
151
172
  const indicatorHeight = 4.5
152
173
  if (drawInterbaseCounts) {
153
- interbaseEvents.reduce((curr, [base, { total }]) => {
174
+ let curr = 0
175
+ for (let i = 0; i < interbaseEvents.length; i++) {
176
+ const base = interbaseEvents[i]
177
+ const { total } = snpinfo.noncov[base]
154
178
  ctx.fillStyle = colorForBase[base]
155
179
  ctx.fillRect(
156
- leftPx - 0.6,
157
- indicatorHeight + snpToHeight(curr),
180
+ leftPx - 0.6 + extraHorizontallyFlippedOffset,
181
+ indicatorHeight + indicatorToHeight(curr),
158
182
  1.2,
159
- snpToHeight(total),
183
+ indicatorToHeight(total),
160
184
  )
161
- return curr + total
162
- }, 0)
185
+ curr += total
186
+ }
163
187
  }
164
188
 
165
189
  if (drawIndicators) {
166
190
  let accum = 0
167
191
  let max = 0
168
192
  let maxBase = ''
169
- interbaseEvents.forEach(([base, { total }]) => {
193
+ for (let i = 0; i < interbaseEvents.length; i++) {
194
+ const base = interbaseEvents[i]
195
+ const { total } = snpinfo.noncov[base]
170
196
  accum += total
171
197
  if (total > max) {
172
198
  max = total
173
199
  maxBase = base
174
200
  }
175
- })
201
+ }
176
202
 
177
203
  // avoid drawing a bunch of indicators if coverage is very low e.g.
178
- // less than 7
179
- if (accum > totalScore * indicatorThreshold && totalScore > 7) {
204
+ // less than 7, uses the prev total in the case of the "cliff"
205
+ const indicatorComparatorScore = Math.max(totalScore, prevTotal)
206
+ if (
207
+ accum > indicatorComparatorScore * indicatorThreshold &&
208
+ indicatorComparatorScore > 7
209
+ ) {
180
210
  ctx.fillStyle = colorForBase[maxBase]
181
211
  ctx.beginPath()
182
- ctx.moveTo(leftPx - 3, 0)
183
- ctx.lineTo(leftPx + 3, 0)
184
- ctx.lineTo(leftPx, indicatorHeight)
212
+ const l = leftPx + extraHorizontallyFlippedOffset
213
+ ctx.moveTo(l - 3.5, 0)
214
+ ctx.lineTo(l + 3.5, 0)
215
+ ctx.lineTo(l, indicatorHeight)
185
216
  ctx.fill()
186
217
  }
187
218
  }
188
- })
189
-
190
- ctx.globalAlpha = 0.7
219
+ prevTotal = totalScore
220
+ }
191
221
 
192
222
  if (drawArcs) {
193
- skips.forEach(f => {
223
+ for (let i = 0; i < skips.length; i++) {
224
+ const f = skips[i]
194
225
  const [left, right] = bpSpanPx(
195
226
  f.get('start'),
196
227
  f.get('end'),
@@ -201,9 +232,9 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
201
232
  ctx.beginPath()
202
233
  const str = f.get('strand') as number
203
234
  const xs = f.get('xs') as string
204
- const pos = 'rgb(255,200,200)'
205
- const neg = 'rgb(200,200,255)'
206
- const neutral = 'rgb(200,200,200)'
235
+ const pos = 'rgba(255,200,200,0.7)'
236
+ const neg = 'rgba(200,200,255,0.7)'
237
+ const neutral = 'rgba(200,200,200,0.7)'
207
238
 
208
239
  if (xs === '+') {
209
240
  ctx.strokeStyle = pos
@@ -221,13 +252,13 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
221
252
  ctx.moveTo(left, height - offset * 2)
222
253
  ctx.bezierCurveTo(left, 0, right, 0, right, height - offset * 2)
223
254
  ctx.stroke()
224
- })
255
+ }
225
256
  }
226
257
 
227
258
  if (displayCrossHatches) {
228
259
  ctx.lineWidth = 1
229
260
  ctx.strokeStyle = 'rgba(140,140,140,0.8)'
230
- values.forEach(tick => {
261
+ ticks.values.forEach(tick => {
231
262
  ctx.beginPath()
232
263
  ctx.moveTo(0, Math.round(toY(tick)))
233
264
  ctx.lineTo(width, Math.round(toY(tick)))
@@ -12,7 +12,7 @@ export default ConfigurationSchema(
12
12
  type: 'number',
13
13
  description:
14
14
  'the proportion of reads containing a insertion/clip indicator',
15
- defaultValue: 0.3,
15
+ defaultValue: 0.4,
16
16
  },
17
17
  drawArcs: {
18
18
  type: 'boolean',
package/src/util.ts CHANGED
@@ -1,4 +1,7 @@
1
+ import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
2
+ import { toArray } from 'rxjs/operators'
1
3
  import { Feature } from '@jbrowse/core/util/simpleFeature'
4
+ import { AugmentedRegion } from '@jbrowse/core/util'
2
5
  // get tag from BAM or CRAM feature, where CRAM uses feature.get('tags') and
3
6
  // BAM does not
4
7
  export function getTag(feature: Feature, tag: string) {
@@ -76,3 +79,25 @@ export function getColorWGBS(strand: number, base: string) {
76
79
  }
77
80
  return '#888'
78
81
  }
82
+
83
+ export async function fetchSequence(
84
+ region: AugmentedRegion,
85
+ adapter: BaseFeatureDataAdapter,
86
+ ) {
87
+ const { end, originalRefName, refName } = region
88
+
89
+ const feats = await adapter
90
+ .getFeatures({
91
+ ...region,
92
+ refName: originalRefName || refName,
93
+ end: end + 1,
94
+ })
95
+ .pipe(toArray())
96
+ .toPromise()
97
+ return feats[0]?.get('seq')
98
+ }
99
+
100
+ // has to check underlying C-G (aka CpG) on the reference sequence
101
+ export function shouldFetchReferenceSequence(type?: string) {
102
+ return type === 'methylation'
103
+ }