jbrowse-plugin-mafviewer 1.1.3 → 1.2.0

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 (62) hide show
  1. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +5 -4
  2. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +103 -82
  3. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -1
  4. package/dist/BgzipTaffyAdapter/util.d.ts +1 -0
  5. package/dist/BgzipTaffyAdapter/util.js +22 -0
  6. package/dist/BgzipTaffyAdapter/util.js.map +1 -0
  7. package/dist/BigMafAdapter/BigMafAdapter.d.ts +3 -2
  8. package/dist/BigMafAdapter/BigMafAdapter.js +4 -3
  9. package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
  10. package/dist/LinearMafDisplay/components/ColorLegend.js +1 -1
  11. package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
  12. package/dist/LinearMafDisplay/components/ReactComponent.js +1 -1
  13. package/dist/LinearMafDisplay/components/ReactComponent.js.map +1 -1
  14. package/dist/LinearMafDisplay/components/SvgWrapper.js +2 -2
  15. package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -1
  16. package/dist/LinearMafDisplay/components/YScaleBars.js +2 -2
  17. package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
  18. package/dist/LinearMafDisplay/renderSvg.d.ts +2 -2
  19. package/dist/LinearMafDisplay/renderSvg.js +0 -1
  20. package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
  21. package/dist/LinearMafDisplay/stateModel.d.ts +19 -17
  22. package/dist/LinearMafDisplay/stateModel.js +17 -10
  23. package/dist/LinearMafDisplay/stateModel.js.map +1 -1
  24. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +8 -4
  25. package/dist/LinearMafRenderer/LinearMafRenderer.js +14 -148
  26. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
  27. package/dist/LinearMafRenderer/configSchema.d.ts +6 -1
  28. package/dist/LinearMafRenderer/configSchema.js +6 -1
  29. package/dist/LinearMafRenderer/configSchema.js.map +1 -1
  30. package/dist/LinearMafRenderer/makeImageData.d.ts +20 -0
  31. package/dist/LinearMafRenderer/makeImageData.js +144 -0
  32. package/dist/LinearMafRenderer/makeImageData.js.map +1 -0
  33. package/dist/LinearMafRenderer/util.d.ts +6 -1
  34. package/dist/LinearMafRenderer/util.js +17 -0
  35. package/dist/LinearMafRenderer/util.js.map +1 -1
  36. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.d.ts +1 -1
  37. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
  38. package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +9 -6
  39. package/dist/MafTabixAdapter/MafTabixAdapter.js +74 -43
  40. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
  41. package/dist/MafTabixAdapter/configSchema.d.ts +7 -0
  42. package/dist/MafTabixAdapter/configSchema.js +7 -0
  43. package/dist/MafTabixAdapter/configSchema.js.map +1 -1
  44. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +7 -8
  45. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
  46. package/package.json +2 -2
  47. package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +123 -86
  48. package/src/BgzipTaffyAdapter/util.ts +25 -0
  49. package/src/BigMafAdapter/BigMafAdapter.ts +10 -4
  50. package/src/LinearMafDisplay/components/ColorLegend.tsx +1 -2
  51. package/src/LinearMafDisplay/components/ReactComponent.tsx +1 -1
  52. package/src/LinearMafDisplay/components/SvgWrapper.tsx +2 -2
  53. package/src/LinearMafDisplay/components/YScaleBars.tsx +3 -2
  54. package/src/LinearMafDisplay/renderSvg.tsx +5 -5
  55. package/src/LinearMafDisplay/stateModel.ts +30 -24
  56. package/src/LinearMafRenderer/LinearMafRenderer.ts +21 -176
  57. package/src/LinearMafRenderer/configSchema.ts +6 -1
  58. package/src/LinearMafRenderer/makeImageData.ts +211 -0
  59. package/src/LinearMafRenderer/util.ts +28 -1
  60. package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +2 -1
  61. package/src/MafTabixAdapter/MafTabixAdapter.ts +92 -44
  62. package/src/MafTabixAdapter/configSchema.ts +7 -0
@@ -1,14 +1,12 @@
1
1
  import { FeatureRendererType } from '@jbrowse/core/pluggableElementTypes'
2
2
  import { RenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
3
- import { createJBrowseTheme } from '@jbrowse/core/ui'
4
3
  import {
5
- Feature,
6
4
  Region,
7
- featureSpanPx,
8
5
  renderToAbstractCanvas,
6
+ updateStatus,
9
7
  } from '@jbrowse/core/util'
10
8
 
11
- import { getColorBaseMap, getContrastBaseMap } from './util'
9
+ import { makeImageData } from './makeImageData'
12
10
 
13
11
  interface Sample {
14
12
  id: string
@@ -20,172 +18,9 @@ interface RenderArgs extends RenderArgsDeserialized {
20
18
  rowProportion: number
21
19
  showAllLetters: boolean
22
20
  mismatchRendering: boolean
21
+ statusCallback?: (arg: string) => void
23
22
  }
24
23
 
25
- function makeImageData({
26
- ctx,
27
- renderArgs,
28
- }: {
29
- ctx: CanvasRenderingContext2D
30
- renderArgs: RenderArgs & { features: Map<string, Feature> }
31
- }) {
32
- const {
33
- regions,
34
- bpPerPx,
35
- rowHeight,
36
- showAllLetters,
37
- theme: configTheme,
38
- mismatchRendering,
39
- samples,
40
- rowProportion,
41
- features,
42
- } = renderArgs
43
- const region = regions[0]!
44
- const h = rowHeight * rowProportion
45
- const theme = createJBrowseTheme(configTheme)
46
- const colorForBase = getColorBaseMap(theme)
47
- const contrastForBase = getContrastBaseMap(theme)
48
- const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]))
49
- const scale = 1 / bpPerPx
50
- const f = 0.4
51
- const h2 = rowHeight / 2
52
- const hp2 = h / 2
53
- const offset = (rowHeight - h) / 2
54
-
55
- // sample as alignments
56
- ctx.font = 'bold 10px Courier New,monospace'
57
-
58
- for (const feature of features.values()) {
59
- const [leftPx] = featureSpanPx(feature, region, bpPerPx)
60
- const vals = feature.get('alignments') as Record<string, { data: string }>
61
- const seq = feature.get('seq').toLowerCase()
62
- for (const [sample, val] of Object.entries(vals)) {
63
- const origAlignment = val.data
64
- const alignment = origAlignment.toLowerCase()
65
-
66
- const row = sampleToRowMap.get(sample)
67
- if (row === undefined) {
68
- continue
69
- }
70
-
71
- const t = rowHeight * row
72
-
73
- // gaps
74
- ctx.beginPath()
75
- ctx.fillStyle = 'black'
76
- for (let i = 0, o = 0; i < alignment.length; i++) {
77
- if (seq[i] !== '-') {
78
- if (alignment[i] === '-') {
79
- const l = leftPx + scale * o
80
- ctx.moveTo(l, t + h2)
81
- ctx.lineTo(l + scale + f, t + h2)
82
- }
83
- o++
84
- }
85
- }
86
- ctx.stroke()
87
-
88
- if (!showAllLetters) {
89
- // matches
90
- ctx.beginPath()
91
- ctx.fillStyle = 'lightgrey'
92
- for (let i = 0, o = 0; i < alignment.length; i++) {
93
- if (seq[i] !== '-') {
94
- const c = alignment[i]
95
- const l = leftPx + scale * o
96
- if (seq[i] === c && c !== '-') {
97
- ctx.rect(l, offset + t, scale + f, h)
98
- }
99
- o++
100
- }
101
- }
102
- ctx.fill()
103
- }
104
-
105
- // mismatches
106
- for (let i = 0, o = 0; i < alignment.length; i++) {
107
- const c = alignment[i]
108
- if (seq[i] !== '-') {
109
- if (c !== '-') {
110
- const l = leftPx + scale * o
111
- if (seq[i] !== c && c !== ' ') {
112
- ctx.fillStyle = mismatchRendering
113
- ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
114
- (colorForBase[c as keyof typeof colorForBase] ?? 'black')
115
- : 'orange'
116
- ctx.fillRect(l, offset + t, scale + f, h)
117
- } else if (showAllLetters) {
118
- ctx.fillStyle = mismatchRendering
119
- ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
120
- (colorForBase[c as keyof typeof colorForBase] ?? 'black')
121
- : 'lightblue'
122
- ctx.fillRect(l, offset + t, scale + f, h)
123
- }
124
- }
125
- o++
126
- }
127
- }
128
-
129
- // font
130
- const charSizeW = 10
131
- if (scale >= charSizeW) {
132
- for (let i = 0, o = 0; i < alignment.length; i++) {
133
- if (seq[i] !== '-') {
134
- const l = leftPx + scale * o
135
- const offset = (scale - charSizeW) / 2 + 1
136
- const c = alignment[i]!
137
- if ((showAllLetters || seq[i] !== c) && c !== '-') {
138
- ctx.fillStyle = mismatchRendering
139
- ? (contrastForBase[c] ?? 'white')
140
- : 'black'
141
- ctx.fillText(origAlignment[i] || '', l + offset, hp2 + t + 3)
142
- }
143
- o++
144
- }
145
- }
146
- }
147
- }
148
- }
149
-
150
- // second pass for insertions, has slightly improved look since the
151
- // insertions are always 'on top' of the other features
152
- for (const feature of features.values()) {
153
- const [leftPx] = featureSpanPx(feature, region, bpPerPx)
154
- const vals = feature.get('alignments') as Record<string, { data: string }>
155
- const seq = feature.get('seq').toLowerCase()
156
-
157
- for (const [sample, val] of Object.entries(vals)) {
158
- const origAlignment = val.data
159
- const alignment = origAlignment.toLowerCase()
160
- const row = sampleToRowMap.get(sample)
161
- if (row === undefined) {
162
- continue
163
- }
164
-
165
- const t = rowHeight * row
166
-
167
- ctx.beginPath()
168
- ctx.fillStyle = 'purple'
169
- for (let i = 0, o = 0; i < alignment.length; i++) {
170
- let ins = ''
171
- while (seq[i] === '-') {
172
- if (alignment[i] !== '-' && alignment[i] !== ' ') {
173
- ins += alignment[i]
174
- }
175
- i++
176
- }
177
- if (ins.length > 0) {
178
- const l = leftPx + scale * o - 1
179
- ctx.rect(l, offset + t + 1, 1, h - 1)
180
- ctx.rect(l - 2, offset + t, 5, 1)
181
- ctx.rect(l - 2, offset + t + h - 1, 5, 1)
182
- }
183
- o++
184
- }
185
- ctx.fill()
186
- }
187
- }
188
- }
189
24
  export default class LinearMafRenderer extends FeatureRendererType {
190
25
  getExpandedRegion(region: Region) {
191
26
  const { start, end } = region
@@ -199,7 +34,13 @@ export default class LinearMafRenderer extends FeatureRendererType {
199
34
  }
200
35
  }
201
36
  async render(renderProps: RenderArgs) {
202
- const { regions, bpPerPx, samples, rowHeight } = renderProps
37
+ const {
38
+ statusCallback = () => {},
39
+ regions,
40
+ bpPerPx,
41
+ samples,
42
+ rowHeight,
43
+ } = renderProps
203
44
  const region = regions[0]!
204
45
  const height = samples.length * (rowHeight + 1) + 100
205
46
  const width = (region.end - region.start) / bpPerPx
@@ -208,13 +49,15 @@ export default class LinearMafRenderer extends FeatureRendererType {
208
49
  width,
209
50
  height,
210
51
  renderProps,
211
- ctx => {
212
- makeImageData({
213
- ctx,
214
- renderArgs: {
215
- ...renderProps,
216
- features,
217
- },
52
+ async ctx => {
53
+ await updateStatus('Rendering alignment', statusCallback, () => {
54
+ makeImageData({
55
+ ctx,
56
+ renderArgs: {
57
+ ...renderProps,
58
+ features,
59
+ },
60
+ })
218
61
  })
219
62
  return undefined
220
63
  },
@@ -228,8 +71,10 @@ export default class LinearMafRenderer extends FeatureRendererType {
228
71
  return {
229
72
  ...results,
230
73
  ...res,
74
+ features: new Map(),
231
75
  width,
232
76
  height,
77
+ containsNoTransferables: true,
233
78
  }
234
79
  }
235
80
  }
@@ -7,7 +7,12 @@ function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
7
7
 
8
8
  const configSchema = ConfigurationSchema(
9
9
  'LinearMafRenderer',
10
- {},
10
+ {
11
+ baseColor: {
12
+ type: 'color',
13
+ defaultValue: 'lightgrey',
14
+ },
15
+ },
11
16
  {
12
17
  /**
13
18
  * #baseConfiguration
@@ -0,0 +1,211 @@
1
+ import { RenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
2
+ import { createJBrowseTheme } from '@jbrowse/core/ui'
3
+ import { Feature, featureSpanPx } from '@jbrowse/core/util'
4
+
5
+ import {
6
+ fillRect,
7
+ getCharWidthHeight,
8
+ getColorBaseMap,
9
+ getContrastBaseMap,
10
+ } from './util'
11
+
12
+ interface Sample {
13
+ id: string
14
+ color?: string
15
+ }
16
+ interface RenderArgs extends RenderArgsDeserialized {
17
+ samples: Sample[]
18
+ rowHeight: number
19
+ rowProportion: number
20
+ showAllLetters: boolean
21
+ mismatchRendering: boolean
22
+ features: Map<string, Feature>
23
+ statusCallback?: (arg: string) => void
24
+ }
25
+
26
+ export function makeImageData({
27
+ ctx,
28
+ renderArgs,
29
+ }: {
30
+ ctx: CanvasRenderingContext2D
31
+ renderArgs: RenderArgs
32
+ }) {
33
+ const {
34
+ regions,
35
+ bpPerPx,
36
+ rowHeight,
37
+ showAllLetters,
38
+ theme: configTheme,
39
+ mismatchRendering,
40
+ samples,
41
+ rowProportion,
42
+ features,
43
+ statusCallback,
44
+ } = renderArgs
45
+ const region = regions[0]!
46
+ const canvasWidth = (region.end - region.start) / bpPerPx
47
+ const h = rowHeight * rowProportion
48
+ const theme = createJBrowseTheme(configTheme)
49
+ const colorForBase = getColorBaseMap(theme)
50
+ const contrastForBase = getContrastBaseMap(theme)
51
+
52
+ const { charHeight } = getCharWidthHeight()
53
+ const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]))
54
+ const scale = 1 / bpPerPx
55
+ const f = 0.4
56
+ const h2 = rowHeight / 2
57
+ const hp2 = h / 2
58
+ const offset = (rowHeight - h) / 2
59
+
60
+ // sample as alignments
61
+ ctx.font = 'bold 10px Courier New,monospace'
62
+
63
+ for (const feature of features.values()) {
64
+ const [leftPx] = featureSpanPx(feature, region, bpPerPx)
65
+ const vals = feature.get('alignments') as Record<string, { data: string }>
66
+ const seq = feature.get('seq').toLowerCase()
67
+ const r = Object.entries(vals)
68
+ for (const [sample, val] of r) {
69
+ const origAlignment = val.data
70
+ const alignment = origAlignment.toLowerCase()
71
+
72
+ const row = sampleToRowMap.get(sample)
73
+ if (row === undefined) {
74
+ continue
75
+ }
76
+
77
+ const t = rowHeight * row
78
+
79
+ // gaps
80
+ ctx.beginPath()
81
+ ctx.fillStyle = 'black'
82
+ for (let i = 0, o = 0; i < alignment.length; i++) {
83
+ if (seq[i] !== '-') {
84
+ if (alignment[i] === '-') {
85
+ const l = leftPx + scale * o
86
+ ctx.moveTo(l, t + h2)
87
+ ctx.lineTo(l + scale + f, t + h2)
88
+ }
89
+ o++
90
+ }
91
+ }
92
+ ctx.stroke()
93
+
94
+ if (!showAllLetters) {
95
+ // matches
96
+ ctx.fillStyle = 'lightgrey'
97
+ for (let i = 0, o = 0; i < alignment.length; i++) {
98
+ if (seq[i] !== '-') {
99
+ const c = alignment[i]
100
+ const l = leftPx + scale * o
101
+ if (seq[i] === c && c !== '-') {
102
+ fillRect(ctx, l, offset + t, scale + f, h, canvasWidth)
103
+ }
104
+ o++
105
+ }
106
+ }
107
+ }
108
+
109
+ // mismatches
110
+ for (let i = 0, o = 0; i < alignment.length; i++) {
111
+ const c = alignment[i]
112
+ if (seq[i] !== '-') {
113
+ if (c !== '-') {
114
+ const l = leftPx + scale * o
115
+ if (seq[i] !== c && c !== ' ') {
116
+ fillRect(
117
+ ctx,
118
+ l,
119
+ offset + t,
120
+ scale + f,
121
+ h,
122
+ canvasWidth,
123
+ mismatchRendering
124
+ ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
125
+ (colorForBase[c as keyof typeof colorForBase] ?? 'black')
126
+ : 'orange',
127
+ )
128
+ } else if (showAllLetters) {
129
+ fillRect(
130
+ ctx,
131
+ l,
132
+ offset + t,
133
+ scale + f,
134
+ h,
135
+ canvasWidth,
136
+ mismatchRendering
137
+ ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
138
+ (colorForBase[c as keyof typeof colorForBase] ?? 'black')
139
+ : 'lightblue',
140
+ )
141
+ }
142
+ }
143
+ o++
144
+ }
145
+ }
146
+
147
+ // font
148
+ const charSizeW = 10
149
+ if (scale >= charSizeW) {
150
+ for (let i = 0, o = 0; i < alignment.length; i++) {
151
+ if (seq[i] !== '-') {
152
+ const l = leftPx + scale * o
153
+ const offset = (scale - charSizeW) / 2 + 1
154
+ const c = alignment[i]!
155
+ if ((showAllLetters || seq[i] !== c) && c !== '-') {
156
+ ctx.fillStyle = mismatchRendering
157
+ ? (contrastForBase[c] ?? 'white')
158
+ : 'black'
159
+ if (rowHeight > charHeight) {
160
+ ctx.fillText(origAlignment[i] || '', l + offset, hp2 + t + 3)
161
+ }
162
+ }
163
+ o++
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ // second pass for insertions, has slightly improved look since the
171
+ // insertions are always 'on top' of the other features
172
+ for (const feature of features.values()) {
173
+ const [leftPx] = featureSpanPx(feature, region, bpPerPx)
174
+ const vals = feature.get('alignments') as Record<string, { data: string }>
175
+ const seq = feature.get('seq').toLowerCase()
176
+
177
+ for (const [sample, val] of Object.entries(vals)) {
178
+ const origAlignment = val.data
179
+ const alignment = origAlignment.toLowerCase()
180
+ const row = sampleToRowMap.get(sample)
181
+ if (row === undefined) {
182
+ continue
183
+ }
184
+
185
+ const t = rowHeight * row
186
+
187
+ ctx.beginPath()
188
+ ctx.fillStyle = 'purple'
189
+ for (let i = 0, o = 0; i < alignment.length; i++) {
190
+ let ins = ''
191
+ while (seq[i] === '-') {
192
+ if (alignment[i] !== '-' && alignment[i] !== ' ') {
193
+ ins += alignment[i]
194
+ }
195
+ i++
196
+ }
197
+ if (ins.length > 0) {
198
+ const l = leftPx + scale * o - 1
199
+
200
+ ctx.rect(l, offset + t, 1, h)
201
+ if (bpPerPx < 1) {
202
+ ctx.rect(l - 2, offset + t, 5, 1)
203
+ ctx.rect(l - 2, offset + t + h - 1, 5, 1)
204
+ }
205
+ }
206
+ o++
207
+ }
208
+ ctx.fill()
209
+ }
210
+ }
211
+ }
@@ -1,4 +1,5 @@
1
- import { Theme } from '@mui/material'
1
+ import { measureText } from '@jbrowse/core/util'
2
+ import type { Theme } from '@mui/material'
2
3
 
3
4
  export function getContrastBaseMap(theme: Theme) {
4
5
  return Object.fromEntries(
@@ -18,3 +19,29 @@ export function getColorBaseMap(theme: Theme) {
18
19
  t: bases.T.main,
19
20
  }
20
21
  }
22
+
23
+ export function fillRect(
24
+ ctx: CanvasRenderingContext2D,
25
+ l: number,
26
+ t: number,
27
+ w: number,
28
+ h: number,
29
+ cw: number,
30
+ color?: string,
31
+ ) {
32
+ if (l + w < 0 || l > cw) {
33
+ return
34
+ }
35
+ if (color) {
36
+ ctx.fillStyle = color
37
+ }
38
+ ctx.fillRect(l, t, w, h)
39
+ }
40
+
41
+ // get width and height of chars the height is an approximation: width letter M
42
+ // is approximately the height
43
+ export function getCharWidthHeight() {
44
+ const charWidth = measureText('A')
45
+ const charHeight = measureText('M') - 2
46
+ return { charWidth, charHeight }
47
+ }
@@ -7,7 +7,6 @@ import {
7
7
  isSessionModelWithWidgets,
8
8
  isSessionWithAddTracks,
9
9
  } from '@jbrowse/core/util'
10
- import { AddTrackModel } from '@jbrowse/plugin-data-management'
11
10
  import {
12
11
  Button,
13
12
  FormControl,
@@ -21,6 +20,8 @@ import {
21
20
  import { getRoot } from 'mobx-state-tree'
22
21
  import { makeStyles } from 'tss-react/mui'
23
22
 
23
+ import type { AddTrackModel } from '@jbrowse/plugin-data-management'
24
+
24
25
  const useStyles = makeStyles()(theme => ({
25
26
  textbox: {
26
27
  width: '100%',
@@ -1,5 +1,13 @@
1
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
2
- import { Feature, Region, SimpleFeature } from '@jbrowse/core/util'
1
+ import {
2
+ BaseFeatureDataAdapter,
3
+ BaseOptions,
4
+ } from '@jbrowse/core/data_adapters/BaseAdapter'
5
+ import {
6
+ Feature,
7
+ Region,
8
+ SimpleFeature,
9
+ updateStatus,
10
+ } from '@jbrowse/core/util'
3
11
  import { openLocation } from '@jbrowse/core/util/io'
4
12
  import { ObservableCreate } from '@jbrowse/core/util/rxjs'
5
13
  import { getSnapshot } from 'mobx-state-tree'
@@ -20,7 +28,7 @@ interface OrganismRecord {
20
28
  export default class MafTabixAdapter extends BaseFeatureDataAdapter {
21
29
  public setupP?: Promise<{ adapter: BaseFeatureDataAdapter }>
22
30
 
23
- async setup() {
31
+ async setupPre() {
24
32
  const config = this.config
25
33
  if (!this.getSubAdapter) {
26
34
  throw new Error('no getSubAdapter available')
@@ -33,9 +41,9 @@ export default class MafTabixAdapter extends BaseFeatureDataAdapter {
33
41
  adapter: adapter.dataAdapter as BaseFeatureDataAdapter,
34
42
  }
35
43
  }
36
- async setupPre() {
44
+ async setupPre2() {
37
45
  if (!this.setupP) {
38
- this.setupP = this.setup().catch((e: unknown) => {
46
+ this.setupP = this.setupPre().catch((e: unknown) => {
39
47
  this.setupP = undefined
40
48
  throw e
41
49
  })
@@ -43,60 +51,100 @@ export default class MafTabixAdapter extends BaseFeatureDataAdapter {
43
51
  return this.setupP
44
52
  }
45
53
 
46
- async getRefNames() {
47
- const { adapter } = await this.setup()
54
+ async setup(opts?: BaseOptions) {
55
+ const { statusCallback = () => {} } = opts || {}
56
+ return updateStatus('Downloading index', statusCallback, () =>
57
+ this.setupPre2(),
58
+ )
59
+ }
60
+
61
+ async getRefNames(opts?: BaseOptions) {
62
+ const { adapter } = await this.setup(opts)
48
63
  return adapter.getRefNames()
49
64
  }
50
65
 
51
- async getHeader() {
52
- const { adapter } = await this.setup()
66
+ async getHeader(opts?: BaseOptions) {
67
+ const { adapter } = await this.setup(opts)
53
68
  return adapter.getHeader()
54
69
  }
55
70
 
56
- getFeatures(query: Region) {
71
+ getFeatures(query: Region, opts?: BaseOptions) {
72
+ const { statusCallback = () => {} } = opts || {}
57
73
  return ObservableCreate<Feature>(async observer => {
58
- const { adapter } = await this.setup()
59
- const features = await firstValueFrom(
60
- adapter.getFeatures(query).pipe(toArray()),
74
+ const { adapter } = await this.setup(opts)
75
+ const features = await updateStatus(
76
+ 'Downloading alignments',
77
+ statusCallback,
78
+ () => firstValueFrom(adapter.getFeatures(query).pipe(toArray())),
61
79
  )
62
80
 
63
- for (const feature of features) {
64
- const data = (feature.get('field5') as string).split(',')
65
- const alignments = {} as Record<string, OrganismRecord>
66
- const alns = data.map(elt => elt.split(':')[5])
81
+ await updateStatus('Processing alignments', statusCallback, () => {
82
+ let firstAssemblyNameFound = ''
83
+ const refAssemblyName = this.getConf('refAssemblyName')
84
+ for (const feature of features) {
85
+ const data = (feature.get('field5') as string).split(',')
86
+ const alignments = {} as Record<string, OrganismRecord>
67
87
 
68
- for (const [j, elt] of data.entries()) {
69
- const ad = elt.split(':')
70
- const idx = ad[0]!.lastIndexOf('.')
71
- const org = ad[0]!.slice(0, idx)
72
- const last = ad[0]!.slice(idx + 1)
73
- if (org) {
74
- alignments[org] = {
75
- chr: last,
76
- start: +ad[1]!,
77
- srcSize: +ad[2]!,
78
- strand: ad[3] === '-' ? -1 : 1,
79
- unknown: +ad[4]!,
80
- data: alns[j]!,
88
+ for (let j = 0; j < data.length; j++) {
89
+ const elt = data[j]!
90
+ const seq = elt.split(':')[5]!
91
+ const ad = elt.split(':')
92
+ const ag = ad[0]!.split('.')
93
+ const [n1, n2 = '', ...rest] = ag
94
+ let assemblyName
95
+ let last = ''
96
+ if (ag.length === 2) {
97
+ assemblyName = n1
98
+ last = n2!
99
+ } else if (!Number.isNaN(+n2)) {
100
+ assemblyName = `${n1}.${n2}`
101
+ last = rest.join('.')
102
+ } else {
103
+ assemblyName = n1
104
+ last = [n2, ...rest].join('.')
105
+ }
106
+ if (assemblyName) {
107
+ firstAssemblyNameFound = firstAssemblyNameFound || assemblyName
108
+ alignments[assemblyName] = {
109
+ chr: last,
110
+ start: +ad[1]!,
111
+ srcSize: +ad[2]!,
112
+ strand: ad[3] === '-' ? -1 : 1,
113
+ unknown: +ad[4]!,
114
+ data: seq,
115
+ }
81
116
  }
82
117
  }
83
- }
84
118
 
85
- observer.next(
86
- new SimpleFeature({
87
- id: feature.id(),
88
- data: {
89
- start: feature.get('start'),
90
- end: feature.get('end'),
91
- refName: feature.get('refName'),
92
- name: feature.get('name'),
93
- score: feature.get('score'),
119
+ console.log(
120
+ {
94
121
  alignments,
95
- seq: alns[0],
122
+ firstAssemblyNameFound,
123
+ refAssemblyName,
124
+ q: query.assemblyName,
96
125
  },
97
- }),
98
- )
99
- }
126
+ alignments[refAssemblyName],
127
+ alignments[query.assemblyName],
128
+ alignments[firstAssemblyNameFound],
129
+ )
130
+ observer.next(
131
+ new SimpleFeature({
132
+ id: feature.id(),
133
+ data: {
134
+ start: feature.get('start'),
135
+ end: feature.get('end'),
136
+ refName: feature.get('refName'),
137
+ name: feature.get('name'),
138
+ score: feature.get('score'),
139
+ alignments,
140
+ seq:
141
+ alignments[refAssemblyName || query.assemblyName]?.data ||
142
+ alignments[firstAssemblyNameFound]?.data,
143
+ },
144
+ }),
145
+ )
146
+ }
147
+ })
100
148
  observer.complete()
101
149
  })
102
150
  }