jbrowse-plugin-mafviewer 1.2.2 → 1.2.4
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/README.md +6 -1
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +12 -3
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +123 -94
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -1
- package/dist/BigMafAdapter/BigMafAdapter.d.ts +1 -1
- package/dist/BigMafAdapter/BigMafAdapter.js +50 -49
- package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
- package/dist/LinearMafDisplay/components/ColorLegend.d.ts +2 -4
- package/dist/LinearMafDisplay/components/ColorLegend.js +2 -3
- package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
- package/dist/LinearMafDisplay/components/{ReactComponent.d.ts → LinearMafDisplayComponent.d.ts} +1 -1
- package/dist/LinearMafDisplay/components/{ReactComponent.js → LinearMafDisplayComponent.js} +2 -2
- package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -0
- package/dist/LinearMafDisplay/components/SetRowHeightDialog.js +38 -0
- package/dist/LinearMafDisplay/components/SetRowHeightDialog.js.map +1 -0
- package/dist/LinearMafDisplay/components/SvgWrapper.d.ts +1 -1
- package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -1
- package/dist/LinearMafDisplay/components/Tree.d.ts +2 -1
- package/dist/LinearMafDisplay/components/Tree.js +2 -0
- package/dist/LinearMafDisplay/components/Tree.js.map +1 -1
- package/dist/LinearMafDisplay/components/YScaleBars.d.ts +1 -1
- package/dist/LinearMafDisplay/components/YScaleBars.js +1 -10
- package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
- package/dist/LinearMafDisplay/index.js +1 -1
- package/dist/LinearMafDisplay/index.js.map +1 -1
- package/dist/LinearMafDisplay/stateModel.d.ts +20 -19
- package/dist/LinearMafDisplay/stateModel.js +42 -7
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafDisplay/types.d.ts +5 -3
- package/dist/LinearMafDisplay/types.js +1 -15
- package/dist/LinearMafDisplay/types.js.map +1 -1
- package/dist/LinearMafDisplay/util.d.ts +4 -0
- package/dist/LinearMafDisplay/util.js +16 -0
- package/dist/LinearMafDisplay/util.js.map +1 -0
- package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +4 -4
- package/dist/LinearMafRenderer/LinearMafRenderer.js +9 -11
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/makeImageData.js +35 -17
- package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/MafAddTrackWorkflow/index.js +0 -1
- package/dist/MafAddTrackWorkflow/index.js.map +1 -1
- package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +1 -1
- package/dist/MafTabixAdapter/MafTabixAdapter.js +6 -7
- package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
- package/dist/MafTabixAdapter/configSchema.js +29 -1
- package/dist/MafTabixAdapter/configSchema.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +7 -19
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/dist/util.d.ts +2 -2
- package/dist/util.js +5 -1
- package/dist/util.js.map +1 -1
- package/package.json +11 -10
- package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +135 -99
- package/src/BigMafAdapter/BigMafAdapter.ts +52 -49
- package/src/LinearMafDisplay/components/ColorLegend.tsx +11 -7
- package/src/LinearMafDisplay/components/{ReactComponent.tsx → LinearMafDisplayComponent.tsx} +2 -2
- package/src/LinearMafDisplay/components/SetRowHeightDialog.tsx +83 -0
- package/src/LinearMafDisplay/components/SvgWrapper.tsx +1 -2
- package/src/LinearMafDisplay/components/Tree.tsx +5 -1
- package/src/LinearMafDisplay/components/YScaleBars.tsx +3 -21
- package/src/LinearMafDisplay/index.ts +1 -1
- package/src/LinearMafDisplay/stateModel.ts +49 -18
- package/src/LinearMafDisplay/types.ts +4 -24
- package/src/LinearMafDisplay/util.ts +27 -0
- package/src/LinearMafRenderer/LinearMafRenderer.ts +10 -15
- package/src/LinearMafRenderer/makeImageData.ts +48 -18
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +9 -11
- package/src/MafAddTrackWorkflow/index.ts +0 -1
- package/src/MafTabixAdapter/MafTabixAdapter.ts +9 -7
- package/src/MafTabixAdapter/configSchema.ts +29 -1
- package/src/util.ts +6 -2
- package/dist/LinearMafDisplay/components/ReactComponent.js.map +0 -1
- package/dist/LinearMafDisplay/components/SetRowHeight.js +0 -36
- package/dist/LinearMafDisplay/components/SetRowHeight.js.map +0 -1
- package/src/LinearMafDisplay/components/SetRowHeight.tsx +0 -83
- /package/dist/LinearMafDisplay/components/{SetRowHeight.d.ts → SetRowHeightDialog.d.ts} +0 -0
package/dist/util.d.ts
CHANGED
package/dist/util.js
CHANGED
|
@@ -3,7 +3,11 @@ function isStrs(array) {
|
|
|
3
3
|
}
|
|
4
4
|
export function normalize(r) {
|
|
5
5
|
return isStrs(r)
|
|
6
|
-
? r.map(elt => ({
|
|
6
|
+
? r.map(elt => ({
|
|
7
|
+
id: elt,
|
|
8
|
+
label: elt,
|
|
9
|
+
color: undefined,
|
|
10
|
+
}))
|
|
7
11
|
: r;
|
|
8
12
|
}
|
|
9
13
|
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,KAAgB;IAC9B,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,KAAgB;IAC9B,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,CAA8D;IAE9D,OAAO,MAAM,CAAC,CAAC,CAAC;QACd,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACZ,EAAE,EAAE,GAAG;YACP,KAAK,EAAE,GAAG;YACV,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC,CAAA;AACP,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.2.
|
|
2
|
+
"version": "1.2.4",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "jbrowse-plugin-mafviewer",
|
|
5
5
|
"keywords": [
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"@jbrowse/core": "^3.0.1",
|
|
32
32
|
"@jbrowse/plugin-data-management": "^3.0.1",
|
|
33
33
|
"@jbrowse/plugin-linear-genome-view": "^3.0.1",
|
|
34
|
-
"@mui/material": "^
|
|
35
|
-
"@mui/system": "^
|
|
36
|
-
"@mui/x-data-grid": "^
|
|
34
|
+
"@mui/material": "^7.0.1",
|
|
35
|
+
"@mui/system": "^7.0.1",
|
|
36
|
+
"@mui/x-data-grid": "^8.2.0",
|
|
37
37
|
"@types/d3-array": "^3.2.1",
|
|
38
38
|
"@types/d3-hierarchy": "^3.1.7",
|
|
39
|
-
"@types/node": "^22.
|
|
39
|
+
"@types/node": "^22.15.16",
|
|
40
40
|
"@types/react": "^19.0.1",
|
|
41
41
|
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
42
42
|
"@typescript-eslint/parser": "^8.18.0",
|
|
@@ -46,28 +46,29 @@
|
|
|
46
46
|
"eslint-plugin-import": "^2.31.0",
|
|
47
47
|
"eslint-plugin-react": "^7.20.3",
|
|
48
48
|
"eslint-plugin-react-hooks": "^5.1.0",
|
|
49
|
-
"eslint-plugin-unicorn": "^
|
|
49
|
+
"eslint-plugin-unicorn": "^59.0.1",
|
|
50
50
|
"mobx": "^6.0.0",
|
|
51
51
|
"mobx-react": "^9.0.1",
|
|
52
52
|
"mobx-state-tree": "^5.4.1",
|
|
53
53
|
"prettier": "^3.4.2",
|
|
54
|
-
"pretty-bytes": "^
|
|
54
|
+
"pretty-bytes": "^7.0.0",
|
|
55
55
|
"react": "^19.0.0",
|
|
56
56
|
"react-dom": "^19.0.0",
|
|
57
57
|
"rimraf": "^6.0.1",
|
|
58
58
|
"rxjs": "^7.8.1",
|
|
59
59
|
"serve": "^14.2.0",
|
|
60
|
-
"tss-react": "^4.
|
|
60
|
+
"tss-react": "^4.9.18",
|
|
61
61
|
"typescript": "^5.1.6",
|
|
62
62
|
"typescript-eslint": "^8.18.0"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@gmod/bgzf-filehandle": "^
|
|
65
|
+
"@gmod/bgzf-filehandle": "^3.0.2",
|
|
66
|
+
"abortable-promise-cache": "^1.5.0",
|
|
66
67
|
"buffer": "^6.0.3",
|
|
67
68
|
"d3-array": "^3.2.4",
|
|
68
69
|
"d3-hierarchy": "^3.1.2",
|
|
69
70
|
"fast-deep-equal": "^3.1.3",
|
|
70
|
-
"generic-filehandle2": "^
|
|
71
|
+
"generic-filehandle2": "^2.0.1",
|
|
71
72
|
"long": "^5.2.3"
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -9,8 +9,10 @@ import {
|
|
|
9
9
|
SimpleFeature,
|
|
10
10
|
updateStatus,
|
|
11
11
|
} from '@jbrowse/core/util'
|
|
12
|
+
import QuickLRU from '@jbrowse/core/util/QuickLRU'
|
|
12
13
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
13
14
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
15
|
+
import AbortablePromiseCache from 'abortable-promise-cache'
|
|
14
16
|
import Long from 'long'
|
|
15
17
|
|
|
16
18
|
import VirtualOffset from './virtualOffset'
|
|
@@ -20,6 +22,7 @@ import { parseRowInstructions } from './rowInstructions'
|
|
|
20
22
|
import { parseLineByLine } from './util'
|
|
21
23
|
|
|
22
24
|
import type { IndexData, OrganismRecord } from './types'
|
|
25
|
+
|
|
23
26
|
interface Entry {
|
|
24
27
|
type: string
|
|
25
28
|
row: number
|
|
@@ -35,11 +38,125 @@ const toP = (s = 0) => +(+s).toFixed(1)
|
|
|
35
38
|
export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
36
39
|
public setupP?: Promise<IndexData>
|
|
37
40
|
|
|
41
|
+
private cache = new AbortablePromiseCache({
|
|
42
|
+
cache: new QuickLRU({ maxSize: 50 }),
|
|
43
|
+
// @ts-expect-error
|
|
44
|
+
fill: async ({ nextEntry, firstEntry }, signal, statusCallback) => {
|
|
45
|
+
const file = openLocation(this.getConf('tafGzLocation'))
|
|
46
|
+
const response = await file.read(
|
|
47
|
+
nextEntry.virtualOffset.blockPosition -
|
|
48
|
+
firstEntry.virtualOffset.blockPosition,
|
|
49
|
+
firstEntry.virtualOffset.blockPosition,
|
|
50
|
+
)
|
|
51
|
+
const buffer = await unzip(response)
|
|
52
|
+
const slice = buffer.slice(firstEntry.virtualOffset.dataPosition)
|
|
53
|
+
return this.getChunk(slice, {
|
|
54
|
+
statusCallback: statusCallback as (arg: string) => void,
|
|
55
|
+
signal,
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
38
60
|
async getRefNames() {
|
|
39
61
|
const data = await this.setup()
|
|
40
62
|
return Object.keys(data)
|
|
41
63
|
}
|
|
42
64
|
|
|
65
|
+
async getChunk(buffer: Uint8Array, opts?: BaseOptions) {
|
|
66
|
+
const { statusCallback = () => {} } = opts || {}
|
|
67
|
+
const alignments = {} as Record<string, OrganismRecord>
|
|
68
|
+
const data = [] as Entry[]
|
|
69
|
+
let a0: any
|
|
70
|
+
let j = 0
|
|
71
|
+
let b = 0
|
|
72
|
+
parseLineByLine(buffer, line => {
|
|
73
|
+
if (j++ % 100 === 0) {
|
|
74
|
+
statusCallback(
|
|
75
|
+
`Processing ${toP(b / 1_000_000)}/${toP(buffer.length / 1_000_000)}Mb`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
b += line.length
|
|
79
|
+
if (line) {
|
|
80
|
+
const [lineData, rowInstructions] = line.split(' ; ')
|
|
81
|
+
if (rowInstructions) {
|
|
82
|
+
for (const ins of parseRowInstructions(rowInstructions)) {
|
|
83
|
+
if (ins.type === 'i') {
|
|
84
|
+
data.splice(ins.row, 0, ins)
|
|
85
|
+
if (!alignments[ins.asm]) {
|
|
86
|
+
alignments[ins.asm] = {
|
|
87
|
+
start: ins.start,
|
|
88
|
+
strand: ins.strand,
|
|
89
|
+
srcSize: ins.length,
|
|
90
|
+
chr: ins.ref,
|
|
91
|
+
data: '',
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const e = alignments[ins.asm]!
|
|
95
|
+
e.data += ' '.repeat(Math.max(0, j - e.data.length - 1)) // catch it up
|
|
96
|
+
} else if (ins.type === 's') {
|
|
97
|
+
if (!alignments[ins.asm]) {
|
|
98
|
+
alignments[ins.asm] = {
|
|
99
|
+
start: ins.start,
|
|
100
|
+
strand: ins.strand,
|
|
101
|
+
srcSize: ins.length,
|
|
102
|
+
chr: ins.ref,
|
|
103
|
+
data: '',
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const e = alignments[ins.asm]!
|
|
107
|
+
e.data += ' '.repeat(Math.max(0, j - e.data.length - 1)) // catch it up
|
|
108
|
+
data[ins.row] = ins
|
|
109
|
+
} else if (ins.type === 'd') {
|
|
110
|
+
data.splice(ins.row, 1)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// no gaps for now(?)
|
|
114
|
+
// else if (ins.type === 'g') {
|
|
115
|
+
// console.log('g??')
|
|
116
|
+
// } else if (ins.type === 'G') {
|
|
117
|
+
// console.log('G??')
|
|
118
|
+
// }
|
|
119
|
+
}
|
|
120
|
+
if (!a0) {
|
|
121
|
+
a0 = data[0]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const lineLen = lineData!.length
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < lineLen; i++) {
|
|
127
|
+
const letter = lineData![i]
|
|
128
|
+
const r = data[i]
|
|
129
|
+
|
|
130
|
+
if (r) {
|
|
131
|
+
alignments[r.asm]!.data += letter
|
|
132
|
+
} else {
|
|
133
|
+
// not sure why but chr22_KI270731v1_random.taf.gz ends up here
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
if (a0) {
|
|
139
|
+
const row0 = alignments[a0.asm]!
|
|
140
|
+
|
|
141
|
+
// see
|
|
142
|
+
// https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
|
|
143
|
+
// for the significance of row[0]:
|
|
144
|
+
//
|
|
145
|
+
// "An anchor line in TAF is a column from which all sequence
|
|
146
|
+
// coordinates can be deduced without scanning backwards to previous
|
|
147
|
+
// lines "
|
|
148
|
+
return {
|
|
149
|
+
uniqueId: `${row0.start}-${row0.data.length}`,
|
|
150
|
+
start: row0.start,
|
|
151
|
+
end: row0.start + row0.data.length,
|
|
152
|
+
strand: row0.strand,
|
|
153
|
+
alignments,
|
|
154
|
+
seq: row0.data,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return undefined
|
|
158
|
+
}
|
|
159
|
+
|
|
43
160
|
setupPre() {
|
|
44
161
|
if (!this.setupP) {
|
|
45
162
|
this.setupP = this.readTaiFile().catch((e: unknown) => {
|
|
@@ -104,103 +221,23 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
104
221
|
return ObservableCreate<Feature>(async observer => {
|
|
105
222
|
try {
|
|
106
223
|
const byteRanges = await this.setup()
|
|
107
|
-
const
|
|
224
|
+
const feat = await updateStatus(
|
|
108
225
|
'Downloading alignments',
|
|
109
226
|
statusCallback,
|
|
110
227
|
() => this.getLines(query, byteRanges),
|
|
111
228
|
)
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
b += line.length
|
|
125
|
-
if (line) {
|
|
126
|
-
const [lineData, rowInstructions] = line.split(' ; ')
|
|
127
|
-
if (rowInstructions) {
|
|
128
|
-
for (const ins of parseRowInstructions(rowInstructions)) {
|
|
129
|
-
if (ins.type === 'i') {
|
|
130
|
-
data.splice(ins.row, 0, ins)
|
|
131
|
-
if (!alignments[ins.asm]) {
|
|
132
|
-
alignments[ins.asm] = {
|
|
133
|
-
start: ins.start,
|
|
134
|
-
strand: ins.strand,
|
|
135
|
-
srcSize: ins.length,
|
|
136
|
-
chr: ins.ref,
|
|
137
|
-
data: '',
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const e = alignments[ins.asm]!
|
|
141
|
-
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
142
|
-
} else if (ins.type === 's') {
|
|
143
|
-
if (!alignments[ins.asm]) {
|
|
144
|
-
alignments[ins.asm] = {
|
|
145
|
-
start: ins.start,
|
|
146
|
-
strand: ins.strand,
|
|
147
|
-
srcSize: ins.length,
|
|
148
|
-
chr: ins.ref,
|
|
149
|
-
data: '',
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const e = alignments[ins.asm]!
|
|
153
|
-
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
154
|
-
data[ins.row] = ins
|
|
155
|
-
} else if (ins.type === 'd') {
|
|
156
|
-
data.splice(ins.row, 1)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// no gaps for now(?)
|
|
160
|
-
// else if (ins.type === 'g') {
|
|
161
|
-
// }
|
|
162
|
-
// else if (ins.type === 'G') {
|
|
163
|
-
// }
|
|
164
|
-
}
|
|
165
|
-
if (!a0) {
|
|
166
|
-
a0 = data[0]
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const lineLen = lineData!.length
|
|
170
|
-
for (let i = 0; i < lineLen; i++) {
|
|
171
|
-
const letter = lineData![i]
|
|
172
|
-
const r = data[i]
|
|
173
|
-
if (r) {
|
|
174
|
-
alignments[r.asm]!.data += letter
|
|
175
|
-
} else {
|
|
176
|
-
// not sure why but chr22_KI270731v1_random.taf.gz ends up here
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
if (a0) {
|
|
182
|
-
const row0 = alignments[a0.asm]!
|
|
183
|
-
|
|
184
|
-
// see
|
|
185
|
-
// https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
|
|
186
|
-
// for the significance of row[0]:
|
|
187
|
-
//
|
|
188
|
-
// "An anchor line in TAF is a column from which all sequence
|
|
189
|
-
// coordinates can be deduced without scanning backwards to previous
|
|
190
|
-
// lines "
|
|
191
|
-
observer.next(
|
|
192
|
-
new SimpleFeature({
|
|
193
|
-
uniqueId: `${row0.start}-${row0.data.length}`,
|
|
194
|
-
refName: query.refName,
|
|
195
|
-
start: row0.start,
|
|
196
|
-
end: row0.start + row0.data.length,
|
|
197
|
-
strand: row0.strand,
|
|
198
|
-
alignments,
|
|
199
|
-
seq: row0.data,
|
|
200
|
-
}),
|
|
201
|
-
)
|
|
202
|
-
}
|
|
229
|
+
if (feat) {
|
|
230
|
+
observer.next(
|
|
231
|
+
// @ts-expect-error
|
|
232
|
+
new SimpleFeature({
|
|
233
|
+
...feat,
|
|
234
|
+
refName: query.refName,
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
237
|
+
} else {
|
|
238
|
+
console.error('no feature found')
|
|
203
239
|
}
|
|
240
|
+
|
|
204
241
|
statusCallback('')
|
|
205
242
|
observer.complete()
|
|
206
243
|
} catch (e) {
|
|
@@ -226,7 +263,6 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
226
263
|
|
|
227
264
|
// TODO: cache processed large chunks
|
|
228
265
|
async getLines(query: Region, byteRanges: IndexData) {
|
|
229
|
-
const file = openLocation(this.getConf('tafGzLocation'))
|
|
230
266
|
const records = byteRanges[query.refName]
|
|
231
267
|
if (records) {
|
|
232
268
|
let firstEntry
|
|
@@ -255,13 +291,13 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
255
291
|
// file whn you request e.g. out of range region (e.g. taf in chr22:1-100
|
|
256
292
|
// and you are at chr22:200-300)
|
|
257
293
|
if (firstEntry && nextEntry) {
|
|
258
|
-
|
|
259
|
-
nextEntry.
|
|
260
|
-
|
|
261
|
-
|
|
294
|
+
return this.cache.get(
|
|
295
|
+
`${JSON.stringify(nextEntry)}_${JSON.stringify(firstEntry)}`,
|
|
296
|
+
{
|
|
297
|
+
nextEntry,
|
|
298
|
+
firstEntry,
|
|
299
|
+
},
|
|
262
300
|
)
|
|
263
|
-
const buffer = await unzip(response)
|
|
264
|
-
return buffer.slice(firstEntry.virtualOffset.dataPosition)
|
|
265
301
|
}
|
|
266
302
|
}
|
|
267
303
|
return undefined
|
|
@@ -26,12 +26,13 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
26
26
|
if (!this.getSubAdapter) {
|
|
27
27
|
throw new Error('no getSubAdapter available')
|
|
28
28
|
}
|
|
29
|
-
const adapter = await this.getSubAdapter({
|
|
30
|
-
...getSnapshot(this.config),
|
|
31
|
-
type: 'BigBedAdapter',
|
|
32
|
-
})
|
|
33
29
|
return {
|
|
34
|
-
adapter:
|
|
30
|
+
adapter: (
|
|
31
|
+
await this.getSubAdapter({
|
|
32
|
+
...getSnapshot(this.config),
|
|
33
|
+
type: 'BigBedAdapter',
|
|
34
|
+
})
|
|
35
|
+
).dataAdapter as BaseFeatureDataAdapter,
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
async setupPre() {
|
|
@@ -59,59 +60,61 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
59
60
|
return ObservableCreate<Feature>(async observer => {
|
|
60
61
|
const { adapter } = await this.setup()
|
|
61
62
|
const features = await updateStatus(
|
|
62
|
-
'Downloading
|
|
63
|
+
'Downloading alignments',
|
|
63
64
|
statusCallback,
|
|
64
65
|
() => firstValueFrom(adapter.getFeatures(query).pipe(toArray())),
|
|
65
66
|
)
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
await updateStatus('Processing alignments', statusCallback, () => {
|
|
68
|
+
for (const feature of features) {
|
|
69
|
+
const maf = feature.get('mafBlock') as string
|
|
70
|
+
const blocks = maf.split(';')
|
|
71
|
+
let aln: string | undefined
|
|
72
|
+
const alns = [] as string[]
|
|
73
|
+
const alignments = {} as Record<string, OrganismRecord>
|
|
74
|
+
const blocks2 = [] as string[]
|
|
75
|
+
for (const block of blocks) {
|
|
76
|
+
if (block.startsWith('s')) {
|
|
77
|
+
if (aln) {
|
|
78
|
+
alns.push(block.split(/ +/)[6]!)
|
|
79
|
+
blocks2.push(block)
|
|
80
|
+
} else {
|
|
81
|
+
aln = block.split(/ +/)[6]
|
|
82
|
+
alns.push(aln!)
|
|
83
|
+
blocks2.push(block)
|
|
84
|
+
}
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
|
-
}
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
for (let i = 0; i < blocks2.length; i++) {
|
|
89
|
+
const elt = blocks2[i]!
|
|
90
|
+
const ad = elt.split(/ +/)
|
|
91
|
+
const y = ad[1]!.split('.')
|
|
92
|
+
const org = y[0]!
|
|
93
|
+
const chr = y[1]!
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
alignments[org] = {
|
|
96
|
+
chr: chr,
|
|
97
|
+
start: +ad[1]!,
|
|
98
|
+
srcSize: +ad[2]!,
|
|
99
|
+
strand: ad[3] === '+' ? 1 : -1,
|
|
100
|
+
unknown: +ad[4]!,
|
|
101
|
+
data: alns[i]!,
|
|
102
|
+
}
|
|
100
103
|
}
|
|
104
|
+
observer.next(
|
|
105
|
+
new SimpleFeature({
|
|
106
|
+
id: feature.id(),
|
|
107
|
+
data: {
|
|
108
|
+
start: feature.get('start'),
|
|
109
|
+
end: feature.get('end'),
|
|
110
|
+
refName: feature.get('refName'),
|
|
111
|
+
seq: alns[0],
|
|
112
|
+
alignments: alignments,
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
)
|
|
101
116
|
}
|
|
102
|
-
|
|
103
|
-
new SimpleFeature({
|
|
104
|
-
id: feature.id(),
|
|
105
|
-
data: {
|
|
106
|
-
start: feature.get('start'),
|
|
107
|
-
end: feature.get('end'),
|
|
108
|
-
refName: feature.get('refName'),
|
|
109
|
-
seq: alns[0],
|
|
110
|
-
alignments: alignments,
|
|
111
|
-
},
|
|
112
|
-
}),
|
|
113
|
-
)
|
|
114
|
-
}
|
|
117
|
+
})
|
|
115
118
|
observer.complete()
|
|
116
119
|
})
|
|
117
120
|
}
|
|
@@ -2,21 +2,25 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
|
|
5
|
-
import { LinearMafDisplayModel } from '../stateModel'
|
|
6
5
|
import RectBg from './RectBg'
|
|
7
6
|
import Tree from './Tree'
|
|
8
7
|
|
|
8
|
+
import type { LinearMafDisplayModel } from '../stateModel'
|
|
9
|
+
|
|
9
10
|
const ColorLegend = observer(function ({
|
|
10
11
|
model,
|
|
11
|
-
labelWidth,
|
|
12
|
-
svgFontSize,
|
|
13
12
|
}: {
|
|
14
13
|
model: LinearMafDisplayModel
|
|
15
|
-
svgFontSize: number
|
|
16
|
-
labelWidth: number
|
|
17
14
|
}) {
|
|
18
|
-
const {
|
|
19
|
-
|
|
15
|
+
const {
|
|
16
|
+
labelWidth,
|
|
17
|
+
canDisplayLabel,
|
|
18
|
+
totalHeight,
|
|
19
|
+
treeWidth,
|
|
20
|
+
samples = [],
|
|
21
|
+
rowHeight,
|
|
22
|
+
svgFontSize,
|
|
23
|
+
} = model
|
|
20
24
|
const boxHeight = Math.min(20, rowHeight)
|
|
21
25
|
|
|
22
26
|
return (
|
package/src/LinearMafDisplay/components/{ReactComponent.tsx → LinearMafDisplayComponent.tsx}
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import React, { useRef, useState } from 'react'
|
|
2
2
|
|
|
3
|
+
import { SanitizedHTML } from '@jbrowse/core/ui'
|
|
3
4
|
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
|
|
4
|
-
import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML'
|
|
5
5
|
import { getContainingView, getEnv } from '@jbrowse/core/util'
|
|
6
6
|
import { observer } from 'mobx-react'
|
|
7
7
|
import { makeStyles } from 'tss-react/mui'
|
|
8
8
|
|
|
9
9
|
import YScaleBars from './YScaleBars'
|
|
10
|
-
import { LinearMafDisplayModel } from '../stateModel'
|
|
11
10
|
|
|
11
|
+
import type { LinearMafDisplayModel } from '../stateModel'
|
|
12
12
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
13
13
|
|
|
14
14
|
const useStyles = makeStyles()({
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
DialogActions,
|
|
7
|
+
DialogContent,
|
|
8
|
+
TextField,
|
|
9
|
+
Typography,
|
|
10
|
+
} from '@mui/material'
|
|
11
|
+
import { observer } from 'mobx-react'
|
|
12
|
+
import { makeStyles } from 'tss-react/mui'
|
|
13
|
+
|
|
14
|
+
const useStyles = makeStyles()({
|
|
15
|
+
root: {
|
|
16
|
+
width: 500,
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const SetRowHeightDialog = observer(function (props: {
|
|
21
|
+
model: {
|
|
22
|
+
rowHeight?: number
|
|
23
|
+
rowProportion?: number
|
|
24
|
+
setRowHeight: (arg: number) => void
|
|
25
|
+
setRowProportion: (arg: number) => void
|
|
26
|
+
}
|
|
27
|
+
handleClose: () => void
|
|
28
|
+
}) {
|
|
29
|
+
const { model, handleClose } = props
|
|
30
|
+
const { classes } = useStyles()
|
|
31
|
+
const [rowHeight, setRowHeight] = useState(`${model.rowHeight}`)
|
|
32
|
+
const [rowProportion, setRowProportion] = useState(`${model.rowProportion}`)
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Dialog open onClose={handleClose} title="Set row height">
|
|
36
|
+
<form
|
|
37
|
+
onSubmit={event => {
|
|
38
|
+
event.preventDefault()
|
|
39
|
+
model.setRowProportion(+rowProportion)
|
|
40
|
+
model.setRowHeight(+rowHeight)
|
|
41
|
+
handleClose()
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<DialogContent className={classes.root}>
|
|
45
|
+
<Typography>
|
|
46
|
+
Set row height and the proportion of the row height to use for
|
|
47
|
+
drawing each row
|
|
48
|
+
</Typography>
|
|
49
|
+
<TextField
|
|
50
|
+
value={rowHeight}
|
|
51
|
+
helperText="Enter row height"
|
|
52
|
+
autoFocus
|
|
53
|
+
onChange={event => {
|
|
54
|
+
setRowHeight(event.target.value)
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
<TextField
|
|
58
|
+
value={rowProportion}
|
|
59
|
+
helperText="Enter row proportion"
|
|
60
|
+
onChange={event => {
|
|
61
|
+
setRowProportion(event.target.value)
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
<DialogActions>
|
|
65
|
+
<Button variant="contained" color="primary" type="submit">
|
|
66
|
+
Submit
|
|
67
|
+
</Button>
|
|
68
|
+
<Button
|
|
69
|
+
variant="contained"
|
|
70
|
+
color="secondary"
|
|
71
|
+
onClick={() => {
|
|
72
|
+
handleClose()
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
Cancel
|
|
76
|
+
</Button>
|
|
77
|
+
</DialogActions>
|
|
78
|
+
</DialogContent>
|
|
79
|
+
</form>
|
|
80
|
+
</Dialog>
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
export default SetRowHeightDialog
|
|
@@ -3,8 +3,7 @@ import React from 'react'
|
|
|
3
3
|
import { getContainingView } from '@jbrowse/core/util'
|
|
4
4
|
import { observer } from 'mobx-react'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
import { LinearMafDisplayModel } from '../stateModel'
|
|
6
|
+
import type { LinearMafDisplayModel } from '../stateModel'
|
|
8
7
|
|
|
9
8
|
const SvgWrapper = observer(function ({
|
|
10
9
|
children,
|
|
@@ -2,7 +2,9 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import type { LinearMafDisplayModel } from '../stateModel'
|
|
6
|
+
|
|
7
|
+
const Tree = observer(function ({ model }: { model: LinearMafDisplayModel }) {
|
|
6
8
|
const {
|
|
7
9
|
// this is needed for redrawing after zoom change, similar to react-msaview
|
|
8
10
|
// renderTreeCanvas
|
|
@@ -20,7 +22,9 @@ const Tree = observer(function ({ model }: { model: any }) {
|
|
|
20
22
|
const { source, target } = link
|
|
21
23
|
const sy = source.x!
|
|
22
24
|
const ty = target.x!
|
|
25
|
+
// @ts-expect-error
|
|
23
26
|
const tx = showBranchLen ? target.len : target.y
|
|
27
|
+
// @ts-expect-error
|
|
24
28
|
const sx = showBranchLen ? source.len : source.y
|
|
25
29
|
|
|
26
30
|
// 1d line intersection to check if line crosses block at all, this is
|