@jbrowse/plugin-alignments 1.5.8 → 1.6.2
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/dist/BamAdapter/BamAdapter.d.ts +5 -0
- package/dist/BamAdapter/BamSlightlyLazyFeature.d.ts +1 -2
- package/dist/CramAdapter/CramAdapter.d.ts +16 -5
- package/dist/LinearAlignmentsDisplay/models/model.d.ts +2 -1
- package/dist/LinearPileupDisplay/model.d.ts +28 -6
- package/dist/LinearSNPCoverageDisplay/models/model.d.ts +3 -0
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +1 -0
- package/dist/plugin-alignments.cjs.development.js +557 -346
- package/dist/plugin-alignments.cjs.development.js.map +1 -1
- package/dist/plugin-alignments.cjs.production.min.js +1 -1
- package/dist/plugin-alignments.cjs.production.min.js.map +1 -1
- package/dist/plugin-alignments.esm.js +557 -346
- package/dist/plugin-alignments.esm.js.map +1 -1
- package/package.json +3 -3
- package/src/BamAdapter/BamAdapter.ts +56 -35
- package/src/BamAdapter/BamSlightlyLazyFeature.ts +18 -25
- package/src/BamAdapter/configSchema.ts +2 -2
- package/src/CramAdapter/CramAdapter.ts +105 -91
- package/src/CramAdapter/configSchema.ts +5 -1
- package/src/LinearAlignmentsDisplay/models/model.tsx +4 -3
- package/src/LinearPileupDisplay/configSchema.ts +3 -3
- package/src/LinearPileupDisplay/model.ts +8 -7
- package/src/LinearSNPCoverageDisplay/models/configSchema.ts +1 -5
- package/src/LinearSNPCoverageDisplay/models/model.ts +3 -2
- package/src/PileupRenderer/PileupRenderer.tsx +5 -3
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +5 -0
- package/src/index.ts +22 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-alignments",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "JBrowse 2 alignments adapters, tracks, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"useSrc": "node ../../scripts/useSrc.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gmod/bam": "^1.1.
|
|
38
|
+
"@gmod/bam": "^1.1.11",
|
|
39
39
|
"@gmod/cram": "^1.5.9",
|
|
40
40
|
"@material-ui/icons": "^4.9.1",
|
|
41
41
|
"abortable-promise-cache": "^1.1.3",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "92455c6021abd69548496a450983d89f8837860d"
|
|
65
65
|
}
|
|
@@ -4,7 +4,11 @@ import {
|
|
|
4
4
|
BaseOptions,
|
|
5
5
|
} from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
6
6
|
import { Region } from '@jbrowse/core/util/types'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
checkAbortSignal,
|
|
9
|
+
bytesForRegions,
|
|
10
|
+
updateStatus,
|
|
11
|
+
} from '@jbrowse/core/util'
|
|
8
12
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
9
13
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
10
14
|
import { Feature } from '@jbrowse/core/util/simpleFeature'
|
|
@@ -35,7 +39,6 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
35
39
|
const location = readConfObject(this.config, ['index', 'location'])
|
|
36
40
|
const indexType = readConfObject(this.config, ['index', 'indexType'])
|
|
37
41
|
const chunkSizeLimit = readConfObject(this.config, 'chunkSizeLimit')
|
|
38
|
-
const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
|
|
39
42
|
const bam = new BamFile({
|
|
40
43
|
bamFilehandle: openLocation(bamLocation, this.pluginManager),
|
|
41
44
|
csiFilehandle:
|
|
@@ -47,7 +50,7 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
47
50
|
? openLocation(location, this.pluginManager)
|
|
48
51
|
: undefined,
|
|
49
52
|
chunkSizeLimit,
|
|
50
|
-
fetchSizeLimit,
|
|
53
|
+
fetchSizeLimit: 100_000_000,
|
|
51
54
|
})
|
|
52
55
|
|
|
53
56
|
const adapterConfig = readConfObject(this.config, 'sequenceAdapter')
|
|
@@ -70,40 +73,44 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
70
73
|
return bam.getHeaderText(opts)
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
private async
|
|
74
|
-
// note that derived classes may not provide a BAM directly so this is
|
|
75
|
-
// conditional
|
|
76
|
+
private async setupPre(opts?: BaseOptions) {
|
|
76
77
|
const { statusCallback = () => {} } = opts || {}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
78
|
+
const { bam } = await this.configure()
|
|
79
|
+
this.samHeader = await updateStatus(
|
|
80
|
+
'Downloading index',
|
|
81
|
+
statusCallback,
|
|
82
|
+
async () => {
|
|
83
|
+
const samHeader = await bam.getHeader(opts)
|
|
84
|
+
|
|
85
|
+
// use the @SQ lines in the header to figure out the
|
|
86
|
+
// mapping between ref ref ID numbers and names
|
|
87
|
+
const idToName: string[] = []
|
|
88
|
+
const nameToId: Record<string, number> = {}
|
|
89
|
+
samHeader
|
|
90
|
+
.filter(l => l.tag === 'SQ')
|
|
91
|
+
.forEach((sqLine, refId) => {
|
|
92
|
+
sqLine.data.forEach(item => {
|
|
93
|
+
if (item.tag === 'SN') {
|
|
94
|
+
// this is the ref name
|
|
95
|
+
const refName = item.value
|
|
96
|
+
nameToId[refName] = refId
|
|
97
|
+
idToName[refId] = refName
|
|
98
|
+
}
|
|
98
99
|
})
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return { idToName, nameToId }
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
return this.samHeader
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async setup(opts?: BaseOptions) {
|
|
109
|
+
if (!this.setupP) {
|
|
110
|
+
this.setupP = this.setupPre(opts).catch(e => {
|
|
111
|
+
this.setupP = undefined
|
|
112
|
+
throw e
|
|
113
|
+
})
|
|
107
114
|
}
|
|
108
115
|
return this.setupP
|
|
109
116
|
}
|
|
@@ -188,6 +195,20 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
188
195
|
}, signal)
|
|
189
196
|
}
|
|
190
197
|
|
|
198
|
+
async estimateRegionsStats(regions: Region[], opts?: BaseOptions) {
|
|
199
|
+
const { bam } = await this.configure()
|
|
200
|
+
// @ts-ignore
|
|
201
|
+
const index = bam.index
|
|
202
|
+
// this is a method to avoid calling on htsget adapters
|
|
203
|
+
if (index.filehandle !== '?') {
|
|
204
|
+
const bytes = await bytesForRegions(regions, index)
|
|
205
|
+
const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
|
|
206
|
+
return { bytes, fetchSizeLimit }
|
|
207
|
+
} else {
|
|
208
|
+
return super.estimateRegionsStats(regions, opts)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
191
212
|
freeResources(/* { region } */): void {}
|
|
192
213
|
|
|
193
214
|
// depends on setup being called before the BAM constructor
|
|
@@ -34,7 +34,6 @@ export default class BamSlightlyLazyFeature implements Feature {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
_get_flags(): string {
|
|
37
|
-
// @ts-ignore
|
|
38
37
|
return this.record.flags
|
|
39
38
|
}
|
|
40
39
|
|
|
@@ -42,11 +41,6 @@ export default class BamSlightlyLazyFeature implements Feature {
|
|
|
42
41
|
return this.record.isReverseComplemented() ? -1 : 1
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
_get_read_group_id(): number {
|
|
46
|
-
// @ts-ignore
|
|
47
|
-
return this.record.readGroupId
|
|
48
|
-
}
|
|
49
|
-
|
|
50
44
|
_get_pair_orientation() {
|
|
51
45
|
return this.record.isPaired() ? this.record.getPairOrientation() : undefined
|
|
52
46
|
}
|
|
@@ -60,36 +54,35 @@ export default class BamSlightlyLazyFeature implements Feature {
|
|
|
60
54
|
return this.record._refID
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
_get_next_refName()
|
|
57
|
+
_get_next_refName() {
|
|
64
58
|
return this.adapter.refIdToName(this.record._next_refid())
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
_get_next_segment_position()
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}`
|
|
61
|
+
_get_next_segment_position() {
|
|
62
|
+
const { record, adapter } = this
|
|
63
|
+
return record.isPaired()
|
|
64
|
+
? `${adapter.refIdToName(record._next_refid())}:${record._next_pos() + 1}`
|
|
72
65
|
: undefined
|
|
73
66
|
}
|
|
74
67
|
|
|
75
|
-
_get_seq()
|
|
68
|
+
_get_seq() {
|
|
76
69
|
return this.record.getReadBases()
|
|
77
70
|
}
|
|
78
71
|
|
|
79
|
-
_get_MD()
|
|
80
|
-
const md = this.record.get('MD')
|
|
81
|
-
const seq = this.get('seq')
|
|
72
|
+
_get_MD() {
|
|
73
|
+
const md = this.record.get('MD') as string | undefined
|
|
74
|
+
const seq = this.get('seq') as string
|
|
82
75
|
if (!md && seq && this.ref) {
|
|
83
76
|
return generateMD(this.ref, this.record.getReadBases(), this.get('CIGAR'))
|
|
84
77
|
}
|
|
85
78
|
return md
|
|
86
79
|
}
|
|
87
80
|
|
|
88
|
-
qualRaw()
|
|
81
|
+
qualRaw() {
|
|
89
82
|
return this.record.qualRaw()
|
|
90
83
|
}
|
|
91
84
|
|
|
92
|
-
set()
|
|
85
|
+
set() {}
|
|
93
86
|
|
|
94
87
|
tags() {
|
|
95
88
|
const properties = Object.getOwnPropertyNames(
|
|
@@ -115,7 +108,7 @@ export default class BamSlightlyLazyFeature implements Feature {
|
|
|
115
108
|
]
|
|
116
109
|
}
|
|
117
110
|
|
|
118
|
-
id()
|
|
111
|
+
id() {
|
|
119
112
|
return `${this.adapter.id}-${this.record.id()}`
|
|
120
113
|
}
|
|
121
114
|
|
|
@@ -130,19 +123,19 @@ export default class BamSlightlyLazyFeature implements Feature {
|
|
|
130
123
|
return this.record.get(field)
|
|
131
124
|
}
|
|
132
125
|
|
|
133
|
-
_get_refName()
|
|
126
|
+
_get_refName() {
|
|
134
127
|
return this.adapter.refIdToName(this.record.seq_id())
|
|
135
128
|
}
|
|
136
129
|
|
|
137
|
-
parent()
|
|
130
|
+
parent() {
|
|
138
131
|
return undefined
|
|
139
132
|
}
|
|
140
133
|
|
|
141
|
-
children()
|
|
134
|
+
children() {
|
|
142
135
|
return undefined
|
|
143
136
|
}
|
|
144
137
|
|
|
145
|
-
pairedFeature()
|
|
138
|
+
pairedFeature() {
|
|
146
139
|
return false
|
|
147
140
|
}
|
|
148
141
|
|
|
@@ -163,7 +156,7 @@ export default class BamSlightlyLazyFeature implements Feature {
|
|
|
163
156
|
} = {
|
|
164
157
|
cigarAttributeName: 'CIGAR',
|
|
165
158
|
},
|
|
166
|
-
)
|
|
159
|
+
) {
|
|
167
160
|
const { cigarAttributeName } = opts
|
|
168
161
|
let mismatches: Mismatch[] = []
|
|
169
162
|
let cigarOps: string[] = []
|
|
@@ -185,7 +178,7 @@ export default class BamSlightlyLazyFeature implements Feature {
|
|
|
185
178
|
}: {
|
|
186
179
|
cigarAttributeName?: string
|
|
187
180
|
mdAttributeName?: string
|
|
188
|
-
} = {})
|
|
181
|
+
} = {}) {
|
|
189
182
|
let mismatches: Mismatch[] = []
|
|
190
183
|
let cigarOps: string[] = []
|
|
191
184
|
|
|
@@ -25,11 +25,11 @@ export default types.late(() =>
|
|
|
25
25
|
}),
|
|
26
26
|
chunkSizeLimit: {
|
|
27
27
|
type: 'number',
|
|
28
|
-
defaultValue:
|
|
28
|
+
defaultValue: 100_000_000,
|
|
29
29
|
},
|
|
30
30
|
fetchSizeLimit: {
|
|
31
31
|
type: 'number',
|
|
32
|
-
defaultValue:
|
|
32
|
+
defaultValue: 5_000_000,
|
|
33
33
|
},
|
|
34
34
|
sequenceAdapter: {
|
|
35
35
|
type: 'frozen',
|
|
@@ -24,14 +24,13 @@ interface Header {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
27
|
-
|
|
28
|
-
private cram: any
|
|
27
|
+
samHeader: Header = {}
|
|
29
28
|
|
|
30
|
-
private setupP?: Promise<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
private setupP?: Promise<{
|
|
30
|
+
samHeader: Header
|
|
31
|
+
cram: any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
32
|
+
sequenceAdapter: any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
33
|
+
}>
|
|
35
34
|
|
|
36
35
|
// maps a refname to an id
|
|
37
36
|
private seqIdToRefName: string[] | undefined
|
|
@@ -48,14 +47,16 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
48
47
|
if (!craiLocation) {
|
|
49
48
|
throw new Error('missing craiLocation argument')
|
|
50
49
|
}
|
|
51
|
-
|
|
50
|
+
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
const cram: any = new IndexedCramFile({
|
|
52
53
|
cramFilehandle: openLocation(cramLocation, this.pluginManager),
|
|
53
54
|
index: new CraiIndex({
|
|
54
55
|
filehandle: openLocation(craiLocation, this.pluginManager),
|
|
55
56
|
}),
|
|
56
57
|
seqFetch: this.seqFetch.bind(this),
|
|
57
58
|
checkSequenceMD5: false,
|
|
58
|
-
fetchSizeLimit: this
|
|
59
|
+
fetchSizeLimit: 200_000_000, // just make this a large size to avoid hitting it
|
|
59
60
|
})
|
|
60
61
|
// instantiate the sequence adapter
|
|
61
62
|
const sequenceAdapterType = readConfObject(this.config, [
|
|
@@ -67,62 +68,55 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
67
68
|
throw new Error('Error getting subadapter')
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
this.sequenceAdapter = dataAdapter
|
|
75
|
-
} else {
|
|
71
|
+
const seqConf = readConfObject(this.config, 'sequenceAdapter')
|
|
72
|
+
const { dataAdapter: sequenceAdapter } = await this.getSubAdapter(seqConf)
|
|
73
|
+
|
|
74
|
+
if (!(sequenceAdapter instanceof BaseFeatureDataAdapter)) {
|
|
76
75
|
throw new Error(
|
|
77
76
|
`CRAM feature adapters cannot use sequence adapters of type '${sequenceAdapterType}'`,
|
|
78
77
|
)
|
|
79
78
|
}
|
|
80
|
-
|
|
79
|
+
|
|
80
|
+
return { cram, sequenceAdapter }
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
async getHeader(opts?: BaseOptions) {
|
|
84
|
-
await this.configure()
|
|
85
|
-
return
|
|
84
|
+
const { cram } = await this.configure()
|
|
85
|
+
return cram.cram.getHeaderText(opts)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
private async seqFetch(seqId: number, start: number, end: number) {
|
|
89
89
|
start -= 1 // convert from 1-based closed to interbase
|
|
90
90
|
|
|
91
|
-
const
|
|
92
|
-
if (!refSeqStore) {
|
|
93
|
-
return undefined
|
|
94
|
-
}
|
|
91
|
+
const { sequenceAdapter } = await this.configure()
|
|
95
92
|
const refName = this.refIdToOriginalName(seqId) || this.refIdToName(seqId)
|
|
96
93
|
if (!refName) {
|
|
97
94
|
return undefined
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
const
|
|
101
|
-
{
|
|
97
|
+
const seqChunks = await sequenceAdapter
|
|
98
|
+
.getFeatures({
|
|
102
99
|
refName,
|
|
103
100
|
start,
|
|
104
101
|
end,
|
|
105
102
|
assemblyName: '',
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const seqChunks = await features.pipe(toArray()).toPromise()
|
|
103
|
+
})
|
|
104
|
+
.pipe(toArray())
|
|
105
|
+
.toPromise()
|
|
111
106
|
|
|
112
|
-
const
|
|
113
|
-
seqChunks
|
|
107
|
+
const sequence = seqChunks
|
|
114
108
|
.sort((a, b) => a.get('start') - b.get('start'))
|
|
115
|
-
.
|
|
109
|
+
.map(chunk => {
|
|
116
110
|
const chunkStart = chunk.get('start')
|
|
117
111
|
const chunkEnd = chunk.get('end')
|
|
118
112
|
const trimStart = Math.max(start - chunkStart, 0)
|
|
119
113
|
const trimEnd = Math.min(end - chunkStart, chunkEnd - chunkStart)
|
|
120
114
|
const trimLength = trimEnd - trimStart
|
|
121
115
|
const chunkSeq = chunk.get('seq') || chunk.get('residues')
|
|
122
|
-
|
|
116
|
+
return chunkSeq.substr(trimStart, trimLength)
|
|
123
117
|
})
|
|
118
|
+
.join('')
|
|
124
119
|
|
|
125
|
-
const sequence = trimmed.join('')
|
|
126
120
|
if (sequence.length !== end - start) {
|
|
127
121
|
throw new Error(
|
|
128
122
|
`sequence fetch failed: fetching ${refName}:${(
|
|
@@ -135,60 +129,56 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
135
129
|
return sequence
|
|
136
130
|
}
|
|
137
131
|
|
|
138
|
-
private async
|
|
132
|
+
private async setupPre(opts?: BaseOptions) {
|
|
139
133
|
const { statusCallback = () => {} } = opts || {}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const refName = item.value
|
|
159
|
-
nameToId[refName] = refId
|
|
160
|
-
idToName[refId] = refName
|
|
161
|
-
}
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
const readGroups = samHeader
|
|
166
|
-
.filter(l => l.tag === 'RG')
|
|
167
|
-
.map(rgLine => rgLine.data.find(item => item.tag === 'ID')?.value)
|
|
168
|
-
|
|
169
|
-
if (idToName.length) {
|
|
170
|
-
this.samHeader = { idToName, nameToId, readGroups }
|
|
134
|
+
const configured = await this.configure()
|
|
135
|
+
statusCallback('Downloading index')
|
|
136
|
+
const { cram } = configured
|
|
137
|
+
const samHeader: HeaderLine[] = await cram.cram.getSamHeader(opts?.signal)
|
|
138
|
+
|
|
139
|
+
// use the @SQ lines in the header to figure out the
|
|
140
|
+
// mapping between ref ID numbers and names
|
|
141
|
+
const idToName: string[] = []
|
|
142
|
+
const nameToId: Record<string, number> = {}
|
|
143
|
+
samHeader
|
|
144
|
+
.filter(l => l.tag === 'SQ')
|
|
145
|
+
.forEach((sqLine, refId) => {
|
|
146
|
+
sqLine.data.forEach(item => {
|
|
147
|
+
if (item.tag === 'SN') {
|
|
148
|
+
// this is the ref name
|
|
149
|
+
const refName = item.value
|
|
150
|
+
nameToId[refName] = refId
|
|
151
|
+
idToName[refId] = refName
|
|
171
152
|
}
|
|
172
|
-
statusCallback('')
|
|
173
|
-
return this.samHeader
|
|
174
|
-
})
|
|
175
|
-
.catch(e => {
|
|
176
|
-
this.setupP = undefined
|
|
177
|
-
throw e
|
|
178
153
|
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const readGroups = samHeader
|
|
157
|
+
.filter(l => l.tag === 'RG')
|
|
158
|
+
.map(rgLine => rgLine.data.find(item => item.tag === 'ID')?.value)
|
|
159
|
+
|
|
160
|
+
const data = { idToName, nameToId, readGroups }
|
|
161
|
+
statusCallback('')
|
|
162
|
+
this.samHeader = data
|
|
163
|
+
return { samHeader: data, ...configured }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private async setup(opts?: BaseOptions) {
|
|
167
|
+
if (!this.setupP) {
|
|
168
|
+
this.setupP = this.setupPre(opts).catch(e => {
|
|
169
|
+
this.setupP = undefined
|
|
170
|
+
throw e
|
|
171
|
+
})
|
|
179
172
|
}
|
|
180
173
|
return this.setupP
|
|
181
174
|
}
|
|
182
175
|
|
|
183
176
|
async getRefNames(opts?: BaseOptions) {
|
|
184
|
-
await this.setup(opts)
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
if (this.sequenceAdapter) {
|
|
189
|
-
return this.sequenceAdapter.getRefNames()
|
|
177
|
+
const { samHeader } = await this.setup(opts)
|
|
178
|
+
if (!samHeader.idToName) {
|
|
179
|
+
throw new Error('CRAM file has no header lines')
|
|
190
180
|
}
|
|
191
|
-
|
|
181
|
+
return samHeader.idToName
|
|
192
182
|
}
|
|
193
183
|
|
|
194
184
|
// use info from the SAM header if possible, but fall back to using
|
|
@@ -227,27 +217,24 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
227
217
|
const { refName, start, end, originalRefName } = region
|
|
228
218
|
|
|
229
219
|
return ObservableCreate<Feature>(async observer => {
|
|
230
|
-
await this.setup(opts)
|
|
231
|
-
|
|
232
|
-
|
|
220
|
+
const { cram, sequenceAdapter } = await this.setup(opts)
|
|
221
|
+
statusCallback('Downloading alignments')
|
|
222
|
+
if (!this.seqIdToRefName) {
|
|
223
|
+
this.seqIdToRefName = await sequenceAdapter.getRefNames(opts)
|
|
233
224
|
}
|
|
234
225
|
const refId = this.refNameToId(refName)
|
|
235
226
|
if (refId !== undefined) {
|
|
236
227
|
if (originalRefName) {
|
|
237
228
|
this.seqIdToOriginalRefName[refId] = originalRefName
|
|
238
229
|
}
|
|
239
|
-
|
|
240
|
-
const records = await this.cram.getRecordsForRange(
|
|
241
|
-
refId,
|
|
242
|
-
start,
|
|
243
|
-
end,
|
|
244
|
-
opts,
|
|
245
|
-
)
|
|
230
|
+
const records = await cram.getRecordsForRange(refId, start, end, opts)
|
|
246
231
|
checkAbortSignal(signal)
|
|
247
232
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
248
233
|
records.forEach((record: any) => {
|
|
249
234
|
observer.next(this.cramRecordToFeature(record))
|
|
250
235
|
})
|
|
236
|
+
} else {
|
|
237
|
+
console.warn('Unknown refName', refName)
|
|
251
238
|
}
|
|
252
239
|
statusCallback('')
|
|
253
240
|
observer.complete()
|
|
@@ -256,8 +243,35 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
256
243
|
|
|
257
244
|
freeResources(/* { region } */): void {}
|
|
258
245
|
|
|
259
|
-
|
|
260
|
-
cramRecordToFeature(record: any): Feature {
|
|
246
|
+
cramRecordToFeature(record: unknown) {
|
|
261
247
|
return new CramSlightlyLazyFeature(record, this)
|
|
262
248
|
}
|
|
249
|
+
|
|
250
|
+
// we return the configured fetchSizeLimit, and the bytes for the region
|
|
251
|
+
async estimateRegionsStats(regions: Region[], opts?: BaseOptions) {
|
|
252
|
+
const bytes = await this.bytesForRegions(regions, opts)
|
|
253
|
+
const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
|
|
254
|
+
return {
|
|
255
|
+
bytes,
|
|
256
|
+
fetchSizeLimit,
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* get the approximate number of bytes queried from the file for the given
|
|
262
|
+
* query regions
|
|
263
|
+
* @param regions - list of query regions
|
|
264
|
+
*/
|
|
265
|
+
private async bytesForRegions(regions: Region[], _opts?: BaseOptions) {
|
|
266
|
+
const { cram } = await this.configure()
|
|
267
|
+
const blockResults = await Promise.all(
|
|
268
|
+
regions.map(region => {
|
|
269
|
+
const { refName, start, end } = region
|
|
270
|
+
const chrId = this.refNameToId(refName)
|
|
271
|
+
return cram.index.getEntriesForRange(chrId, start, end)
|
|
272
|
+
}),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return blockResults.flat().reduce((a, b) => a + b.sliceBytes, 0)
|
|
276
|
+
}
|
|
263
277
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
2
|
import { ConfigurationSchema } from '@jbrowse/core/configuration'
|
|
3
|
+
import { types } from 'mobx-state-tree'
|
|
3
4
|
|
|
4
5
|
export default (pluginManager: PluginManager) => {
|
|
5
|
-
const { types } = pluginManager.lib['mobx-state-tree']
|
|
6
6
|
return types.late(() =>
|
|
7
7
|
ConfigurationSchema(
|
|
8
8
|
'CramAdapter',
|
|
9
9
|
{
|
|
10
|
+
fetchSizeLimit: {
|
|
11
|
+
type: 'number',
|
|
12
|
+
defaultValue: 3_000_000,
|
|
13
|
+
},
|
|
10
14
|
cramLocation: {
|
|
11
15
|
type: 'fileLocation',
|
|
12
16
|
defaultValue: {
|
|
@@ -32,6 +32,7 @@ const stateModelFactory = (
|
|
|
32
32
|
height: 250,
|
|
33
33
|
showCoverage: true,
|
|
34
34
|
showPileup: true,
|
|
35
|
+
userFeatureScreenDensity: types.maybe(types.number),
|
|
35
36
|
}),
|
|
36
37
|
)
|
|
37
38
|
.volatile(() => ({
|
|
@@ -126,9 +127,9 @@ const stateModelFactory = (
|
|
|
126
127
|
height: self.snpCovHeight,
|
|
127
128
|
}
|
|
128
129
|
},
|
|
129
|
-
|
|
130
|
-
self.PileupDisplay.
|
|
131
|
-
self.SNPCoverageDisplay.
|
|
130
|
+
setUserFeatureScreenDensity(limit: number) {
|
|
131
|
+
self.PileupDisplay.setUserFeatureScreenDensity(limit)
|
|
132
|
+
self.SNPCoverageDisplay.setUserFeatureScreenDensity(limit)
|
|
132
133
|
},
|
|
133
134
|
setPileupDisplay(displayConfig: AnyConfigurationModel) {
|
|
134
135
|
self.PileupDisplay = {
|
|
@@ -23,10 +23,10 @@ function PileupConfigFactory(pluginManager: PluginManager) {
|
|
|
23
23
|
SvgFeatureRenderer: SvgFeatureRendererConfigSchema,
|
|
24
24
|
}),
|
|
25
25
|
renderer: '',
|
|
26
|
-
|
|
26
|
+
maxFeatureScreenDensity: {
|
|
27
27
|
type: 'number',
|
|
28
|
-
description: 'maximum
|
|
29
|
-
defaultValue:
|
|
28
|
+
description: 'maximum features per pixel that is displayed in the view',
|
|
29
|
+
defaultValue: 5,
|
|
30
30
|
},
|
|
31
31
|
colorScheme: {
|
|
32
32
|
type: 'stringEnum',
|
|
@@ -95,15 +95,11 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
|
|
|
95
95
|
colorTagMap: observable.map<string, string>({}),
|
|
96
96
|
modificationTagMap: observable.map<string, string>({}),
|
|
97
97
|
ready: false,
|
|
98
|
-
currBpPerPx: 0,
|
|
99
98
|
}))
|
|
100
99
|
.actions(self => ({
|
|
101
100
|
setReady(flag: boolean) {
|
|
102
101
|
self.ready = flag
|
|
103
102
|
},
|
|
104
|
-
setCurrBpPerPx(n: number) {
|
|
105
|
-
self.currBpPerPx = n
|
|
106
|
-
},
|
|
107
103
|
setMaxHeight(n: number) {
|
|
108
104
|
self.trackMaxHeight = n
|
|
109
105
|
},
|
|
@@ -404,16 +400,21 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
|
|
|
404
400
|
renderProps() {
|
|
405
401
|
const view = getContainingView(self) as LGV
|
|
406
402
|
const {
|
|
407
|
-
ready,
|
|
408
403
|
colorTagMap,
|
|
409
404
|
modificationTagMap,
|
|
410
405
|
sortedBy,
|
|
411
406
|
colorBy,
|
|
412
407
|
rpcDriverName,
|
|
413
408
|
} = self
|
|
409
|
+
|
|
410
|
+
const superProps = superRenderProps()
|
|
411
|
+
|
|
414
412
|
return {
|
|
415
|
-
...
|
|
416
|
-
notReady:
|
|
413
|
+
...superProps,
|
|
414
|
+
notReady:
|
|
415
|
+
superProps.notReady ||
|
|
416
|
+
!self.ready ||
|
|
417
|
+
(sortedBy && self.currBpPerPx !== view.bpPerPx),
|
|
417
418
|
rpcDriverName,
|
|
418
419
|
displayModel: self,
|
|
419
420
|
sortedBy,
|