jbrowse-plugin-mafviewer 1.1.4 → 1.2.1
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/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +5 -4
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +102 -82
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -1
- package/dist/BgzipTaffyAdapter/util.d.ts +1 -0
- package/dist/BgzipTaffyAdapter/util.js +22 -0
- package/dist/BgzipTaffyAdapter/util.js.map +1 -0
- package/dist/BigMafAdapter/BigMafAdapter.d.ts +3 -2
- package/dist/BigMafAdapter/BigMafAdapter.js +4 -3
- package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
- package/dist/LinearMafDisplay/components/ColorLegend.js +1 -1
- package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
- package/dist/LinearMafDisplay/components/ReactComponent.js +1 -1
- package/dist/LinearMafDisplay/components/ReactComponent.js.map +1 -1
- package/dist/LinearMafDisplay/components/SvgWrapper.js +2 -2
- package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -1
- package/dist/LinearMafDisplay/components/YScaleBars.js +2 -2
- package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
- package/dist/LinearMafDisplay/renderSvg.d.ts +2 -2
- package/dist/LinearMafDisplay/renderSvg.js +0 -1
- package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
- package/dist/LinearMafDisplay/stateModel.d.ts +19 -17
- package/dist/LinearMafDisplay/stateModel.js +17 -10
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +8 -4
- package/dist/LinearMafRenderer/LinearMafRenderer.js +14 -148
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/configSchema.d.ts +6 -1
- package/dist/LinearMafRenderer/configSchema.js +6 -1
- package/dist/LinearMafRenderer/configSchema.js.map +1 -1
- package/dist/LinearMafRenderer/makeImageData.d.ts +20 -0
- package/dist/LinearMafRenderer/makeImageData.js +144 -0
- package/dist/LinearMafRenderer/makeImageData.js.map +1 -0
- package/dist/LinearMafRenderer/util.d.ts +6 -1
- package/dist/LinearMafRenderer/util.js +17 -0
- package/dist/LinearMafRenderer/util.js.map +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.d.ts +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +9 -6
- package/dist/MafTabixAdapter/MafTabixAdapter.js +69 -56
- package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
- package/dist/MafTabixAdapter/configSchema.d.ts +7 -0
- package/dist/MafTabixAdapter/configSchema.js +7 -0
- package/dist/MafTabixAdapter/configSchema.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +7 -8
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/package.json +3 -2
- package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +122 -86
- package/src/BgzipTaffyAdapter/util.ts +25 -0
- package/src/BigMafAdapter/BigMafAdapter.ts +10 -4
- package/src/LinearMafDisplay/components/ColorLegend.tsx +1 -2
- package/src/LinearMafDisplay/components/ReactComponent.tsx +1 -1
- package/src/LinearMafDisplay/components/SvgWrapper.tsx +2 -2
- package/src/LinearMafDisplay/components/YScaleBars.tsx +3 -2
- package/src/LinearMafDisplay/renderSvg.tsx +5 -5
- package/src/LinearMafDisplay/stateModel.ts +30 -24
- package/src/LinearMafRenderer/LinearMafRenderer.ts +21 -176
- package/src/LinearMafRenderer/configSchema.ts +6 -1
- package/src/LinearMafRenderer/makeImageData.ts +210 -0
- package/src/LinearMafRenderer/util.ts +29 -1
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +2 -1
- package/src/MafTabixAdapter/MafTabixAdapter.ts +84 -57
- package/src/MafTabixAdapter/configSchema.ts +7 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.1
|
|
2
|
+
"version": "1.2.1",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "jbrowse-plugin-mafviewer",
|
|
5
5
|
"keywords": [
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
42
42
|
"@typescript-eslint/parser": "^8.18.0",
|
|
43
43
|
"chalk": "^5.3.0",
|
|
44
|
-
"esbuild": "^0.
|
|
44
|
+
"esbuild": "^0.25.0",
|
|
45
45
|
"eslint": "^9.17.0",
|
|
46
46
|
"eslint-plugin-import": "^2.31.0",
|
|
47
47
|
"eslint-plugin-react": "^7.20.3",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"buffer": "^6.0.3",
|
|
67
67
|
"d3-array": "^3.2.4",
|
|
68
68
|
"d3-hierarchy": "^3.1.2",
|
|
69
|
+
"fast-deep-equal": "^3.1.3",
|
|
69
70
|
"generic-filehandle2": "^1.0.0",
|
|
70
71
|
"long": "^5.2.3"
|
|
71
72
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { unzip } from '@gmod/bgzf-filehandle'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
BaseFeatureDataAdapter,
|
|
4
|
+
BaseOptions,
|
|
5
|
+
} from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
6
|
+
import {
|
|
7
|
+
Feature,
|
|
8
|
+
Region,
|
|
9
|
+
SimpleFeature,
|
|
10
|
+
updateStatus,
|
|
11
|
+
} from '@jbrowse/core/util'
|
|
4
12
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
5
13
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
6
14
|
import Long from 'long'
|
|
@@ -9,6 +17,7 @@ import VirtualOffset from './virtualOffset'
|
|
|
9
17
|
import parseNewick from '../parseNewick'
|
|
10
18
|
import { normalize } from '../util'
|
|
11
19
|
import { parseRowInstructions } from './rowInstructions'
|
|
20
|
+
import { parseLineByLine } from './util'
|
|
12
21
|
|
|
13
22
|
import type { IndexData, OrganismRecord } from './types'
|
|
14
23
|
interface Entry {
|
|
@@ -20,6 +29,9 @@ interface Entry {
|
|
|
20
29
|
strand: number
|
|
21
30
|
length: number
|
|
22
31
|
}
|
|
32
|
+
|
|
33
|
+
const toP = (s = 0) => +(+s).toFixed(1)
|
|
34
|
+
|
|
23
35
|
export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
24
36
|
public setupP?: Promise<IndexData>
|
|
25
37
|
|
|
@@ -28,7 +40,7 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
28
40
|
return Object.keys(data)
|
|
29
41
|
}
|
|
30
42
|
|
|
31
|
-
|
|
43
|
+
setupPre() {
|
|
32
44
|
if (!this.setupP) {
|
|
33
45
|
this.setupP = this.readTaiFile().catch((e: unknown) => {
|
|
34
46
|
this.setupP = undefined
|
|
@@ -37,6 +49,12 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
37
49
|
}
|
|
38
50
|
return this.setupP
|
|
39
51
|
}
|
|
52
|
+
setup(opts?: BaseOptions) {
|
|
53
|
+
const { statusCallback = () => {} } = opts || {}
|
|
54
|
+
return updateStatus('Downloading index', statusCallback, () =>
|
|
55
|
+
this.setupPre(),
|
|
56
|
+
)
|
|
57
|
+
}
|
|
40
58
|
|
|
41
59
|
async readTaiFile() {
|
|
42
60
|
const text = await openLocation(this.getConf('taiLocation')).readFile(
|
|
@@ -81,91 +99,109 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
81
99
|
return entries
|
|
82
100
|
}
|
|
83
101
|
|
|
84
|
-
getFeatures(query: Region) {
|
|
102
|
+
getFeatures(query: Region, opts?: BaseOptions) {
|
|
103
|
+
const { statusCallback = () => {} } = opts || {}
|
|
85
104
|
return ObservableCreate<Feature>(async observer => {
|
|
86
105
|
try {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const byteRanges = await this.setup()
|
|
107
|
+
const buffer = await updateStatus(
|
|
108
|
+
'Downloading alignments',
|
|
109
|
+
statusCallback,
|
|
110
|
+
() => this.getLines(query, byteRanges),
|
|
111
|
+
)
|
|
112
|
+
if (buffer) {
|
|
113
|
+
const alignments = {} as Record<string, OrganismRecord>
|
|
114
|
+
const data = [] as Entry[]
|
|
115
|
+
let a0: any
|
|
116
|
+
let j = 0
|
|
117
|
+
let b = 0
|
|
118
|
+
parseLineByLine(buffer, line => {
|
|
119
|
+
if (j++ % 100 === 0) {
|
|
120
|
+
statusCallback(
|
|
121
|
+
`Processing ${toP(b / 1_000_000)}/${toP(buffer.length / 1_000_000)}Mb`,
|
|
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
|
+
}
|
|
108
139
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
+
}
|
|
120
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)
|
|
121
157
|
}
|
|
122
|
-
const e = alignments[ins.asm]!
|
|
123
|
-
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
124
|
-
data[ins.row] = ins
|
|
125
|
-
} else if (ins.type === 'd') {
|
|
126
|
-
data.splice(ins.row, 1)
|
|
127
|
-
}
|
|
128
158
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
}
|
|
134
168
|
}
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|
|
137
178
|
}
|
|
138
179
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
)
|
|
145
202
|
}
|
|
146
203
|
}
|
|
147
|
-
|
|
148
|
-
const row0 = alignments[a0.asm]!
|
|
149
|
-
|
|
150
|
-
// see
|
|
151
|
-
// https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
|
|
152
|
-
// for the significance of row[0]:
|
|
153
|
-
//
|
|
154
|
-
// "An anchor line in TAF is a column from which all sequence
|
|
155
|
-
// coordinates can be deduced without scanning backwards to previous
|
|
156
|
-
// lines "
|
|
157
|
-
observer.next(
|
|
158
|
-
new SimpleFeature({
|
|
159
|
-
uniqueId: `${row0.start}-${row0.data.length}`,
|
|
160
|
-
refName: query.refName,
|
|
161
|
-
start: row0.start,
|
|
162
|
-
end: row0.start + row0.data.length,
|
|
163
|
-
strand: row0.strand,
|
|
164
|
-
alignments,
|
|
165
|
-
seq: row0.data,
|
|
166
|
-
}),
|
|
167
|
-
)
|
|
168
|
-
}
|
|
204
|
+
statusCallback('')
|
|
169
205
|
observer.complete()
|
|
170
206
|
} catch (e) {
|
|
171
207
|
observer.error(e)
|
|
@@ -188,15 +224,14 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
188
224
|
}
|
|
189
225
|
}
|
|
190
226
|
|
|
191
|
-
|
|
192
|
-
|
|
227
|
+
// TODO: cache processed large chunks
|
|
228
|
+
async getLines(query: Region, byteRanges: IndexData) {
|
|
193
229
|
const file = openLocation(this.getConf('tafGzLocation'))
|
|
194
|
-
|
|
195
|
-
const decoder = new TextDecoder('utf8')
|
|
196
230
|
const records = byteRanges[query.refName]
|
|
197
231
|
if (records) {
|
|
198
|
-
let firstEntry
|
|
232
|
+
let firstEntry
|
|
199
233
|
let nextEntry
|
|
234
|
+
|
|
200
235
|
// two pass:
|
|
201
236
|
// first pass: find first block greater than query start, then -1 from
|
|
202
237
|
// that
|
|
@@ -216,6 +251,9 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
216
251
|
}
|
|
217
252
|
|
|
218
253
|
nextEntry = nextEntry ?? records.at(-1)
|
|
254
|
+
// we NEED at least a firstEntry (validate behavior?) because othrwise it fetches whole
|
|
255
|
+
// file whn you request e.g. out of range region (e.g. taf in chr22:1-100
|
|
256
|
+
// and you are at chr22:200-300)
|
|
219
257
|
if (firstEntry && nextEntry) {
|
|
220
258
|
const response = await file.read(
|
|
221
259
|
nextEntry.virtualOffset.blockPosition -
|
|
@@ -223,12 +261,10 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
223
261
|
firstEntry.virtualOffset.blockPosition,
|
|
224
262
|
)
|
|
225
263
|
const buffer = await unzip(response)
|
|
226
|
-
return
|
|
227
|
-
.decode(buffer.slice(firstEntry.virtualOffset.dataPosition))
|
|
228
|
-
.split('\n')
|
|
264
|
+
return buffer.slice(firstEntry.virtualOffset.dataPosition)
|
|
229
265
|
}
|
|
230
266
|
}
|
|
231
|
-
return
|
|
267
|
+
return undefined
|
|
232
268
|
}
|
|
233
269
|
|
|
234
270
|
freeResources(): void {}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function parseLineByLine<T>(
|
|
2
|
+
buffer: Uint8Array,
|
|
3
|
+
cb: (line: string) => T | undefined,
|
|
4
|
+
): T[] {
|
|
5
|
+
let blockStart = 0
|
|
6
|
+
const entries: T[] = []
|
|
7
|
+
const decoder = new TextDecoder('utf8')
|
|
8
|
+
while (blockStart < buffer.length) {
|
|
9
|
+
const n = buffer.indexOf(10, blockStart)
|
|
10
|
+
if (n === -1) {
|
|
11
|
+
break
|
|
12
|
+
}
|
|
13
|
+
const b = buffer.subarray(blockStart, n)
|
|
14
|
+
const line = decoder.decode(b).trim()
|
|
15
|
+
if (line) {
|
|
16
|
+
const entry = cb(line)
|
|
17
|
+
if (entry) {
|
|
18
|
+
entries.push(entry)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
blockStart = n + 1
|
|
23
|
+
}
|
|
24
|
+
return entries
|
|
25
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
2
|
-
import {
|
|
2
|
+
import { SimpleFeature, updateStatus } from '@jbrowse/core/util'
|
|
3
3
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
4
4
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
5
5
|
import { getSnapshot } from 'mobx-state-tree'
|
|
@@ -8,6 +8,9 @@ import { firstValueFrom, toArray } from 'rxjs'
|
|
|
8
8
|
import parseNewick from '../parseNewick'
|
|
9
9
|
import { normalize } from '../util'
|
|
10
10
|
|
|
11
|
+
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
12
|
+
import type { Feature, Region } from '@jbrowse/core/util'
|
|
13
|
+
|
|
11
14
|
interface OrganismRecord {
|
|
12
15
|
chr: string
|
|
13
16
|
start: number
|
|
@@ -51,11 +54,14 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
51
54
|
return adapter.getHeader()
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
getFeatures(query: Region) {
|
|
57
|
+
getFeatures(query: Region, opts?: BaseOptions) {
|
|
58
|
+
const { statusCallback = () => {} } = opts || {}
|
|
55
59
|
return ObservableCreate<Feature>(async observer => {
|
|
56
60
|
const { adapter } = await this.setup()
|
|
57
|
-
const features = await
|
|
58
|
-
|
|
61
|
+
const features = await updateStatus(
|
|
62
|
+
'Downloading alignment',
|
|
63
|
+
statusCallback,
|
|
64
|
+
() => firstValueFrom(adapter.getFeatures(query).pipe(toArray())),
|
|
59
65
|
)
|
|
60
66
|
for (const feature of features) {
|
|
61
67
|
const maf = feature.get('mafBlock') as string
|
|
@@ -2,7 +2,6 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
|
|
5
|
-
// locals
|
|
6
5
|
import { LinearMafDisplayModel } from '../stateModel'
|
|
7
6
|
import RectBg from './RectBg'
|
|
8
7
|
import Tree from './Tree'
|
|
@@ -16,7 +15,7 @@ const ColorLegend = observer(function ({
|
|
|
16
15
|
svgFontSize: number
|
|
17
16
|
labelWidth: number
|
|
18
17
|
}) {
|
|
19
|
-
const { totalHeight, treeWidth, samples, rowHeight } = model
|
|
18
|
+
const { totalHeight, treeWidth, samples = [], rowHeight } = model
|
|
20
19
|
const canDisplayLabel = rowHeight >= 8
|
|
21
20
|
const boxHeight = Math.min(20, rowHeight)
|
|
22
21
|
|
|
@@ -18,7 +18,7 @@ const SvgWrapper = observer(function ({
|
|
|
18
18
|
if (exportSVG) {
|
|
19
19
|
return <>{children}</>
|
|
20
20
|
} else {
|
|
21
|
-
const {
|
|
21
|
+
const { totalHeight } = model
|
|
22
22
|
return (
|
|
23
23
|
<svg
|
|
24
24
|
style={{
|
|
@@ -26,7 +26,7 @@ const SvgWrapper = observer(function ({
|
|
|
26
26
|
top: 0,
|
|
27
27
|
left: 0,
|
|
28
28
|
pointerEvents: 'none',
|
|
29
|
-
height:
|
|
29
|
+
height: totalHeight,
|
|
30
30
|
width: getContainingView(model).width,
|
|
31
31
|
}}
|
|
32
32
|
>
|
|
@@ -22,8 +22,9 @@ export const YScaleBars = observer(function (props: {
|
|
|
22
22
|
|
|
23
23
|
const labelWidth = max(
|
|
24
24
|
samples
|
|
25
|
-
|
|
26
|
-
.map(width => (canDisplayLabel ? width : minWidth)),
|
|
25
|
+
?.map(s => measureText(s.label, svgFontSize))
|
|
26
|
+
.map(width => (canDisplayLabel ? width : minWidth)) || [],
|
|
27
|
+
0,
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
return (
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import { getContainingView } from '@jbrowse/core/util'
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import YScaleBars from './components/YScaleBars'
|
|
6
|
+
|
|
7
|
+
import type { LinearMafDisplayModel } from './stateModel'
|
|
8
|
+
import type {
|
|
5
9
|
ExportSvgDisplayOptions,
|
|
6
10
|
LinearGenomeViewModel,
|
|
7
11
|
} from '@jbrowse/plugin-linear-genome-view'
|
|
8
12
|
|
|
9
|
-
// locals
|
|
10
|
-
import YScaleBars from './components/YScaleBars'
|
|
11
|
-
import { LinearMafDisplayModel } from './stateModel'
|
|
12
|
-
|
|
13
13
|
export async function renderSvg(
|
|
14
14
|
self: LinearMafDisplayModel,
|
|
15
15
|
opts: ExportSvgDisplayOptions,
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
AnyConfigurationModel,
|
|
4
|
-
AnyConfigurationSchemaType,
|
|
5
|
-
ConfigurationReference,
|
|
6
|
-
getConf,
|
|
7
|
-
} from '@jbrowse/core/configuration'
|
|
1
|
+
import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
|
|
8
2
|
import { getEnv, getSession } from '@jbrowse/core/util'
|
|
9
3
|
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
|
|
10
|
-
import { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
|
|
11
4
|
import { ascending } from 'd3-array'
|
|
12
|
-
import {
|
|
5
|
+
import { cluster, hierarchy } from 'd3-hierarchy'
|
|
6
|
+
import deepEqual from 'fast-deep-equal'
|
|
13
7
|
import { autorun } from 'mobx'
|
|
14
|
-
import {
|
|
8
|
+
import { addDisposer, isAlive, types } from 'mobx-state-tree'
|
|
15
9
|
|
|
16
10
|
import SetRowHeightDialog from './components/SetRowHeight'
|
|
17
|
-
import {
|
|
18
|
-
NodeWithIds,
|
|
19
|
-
NodeWithIdsAndLength,
|
|
20
|
-
maxLength,
|
|
21
|
-
setBrLength,
|
|
22
|
-
} from './types'
|
|
11
|
+
import { maxLength, setBrLength } from './types'
|
|
23
12
|
import { normalize } from '../util'
|
|
24
13
|
|
|
14
|
+
import type { NodeWithIds, NodeWithIdsAndLength } from './types'
|
|
15
|
+
import type PluginManager from '@jbrowse/core/PluginManager'
|
|
16
|
+
import type {
|
|
17
|
+
AnyConfigurationModel,
|
|
18
|
+
AnyConfigurationSchemaType,
|
|
19
|
+
} from '@jbrowse/core/configuration'
|
|
20
|
+
import type { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
|
|
21
|
+
import type { HierarchyNode } from 'd3-hierarchy'
|
|
22
|
+
import type { Instance } from 'mobx-state-tree'
|
|
23
|
+
|
|
25
24
|
interface Sample {
|
|
26
25
|
id: string
|
|
27
26
|
label: string
|
|
@@ -90,11 +89,11 @@ export default function stateModelFactory(
|
|
|
90
89
|
/**
|
|
91
90
|
* #volatile
|
|
92
91
|
*/
|
|
93
|
-
volatileSamples:
|
|
92
|
+
volatileSamples: undefined as Sample[] | undefined,
|
|
94
93
|
/**
|
|
95
94
|
* #volatile
|
|
96
95
|
*/
|
|
97
|
-
|
|
96
|
+
volatileTree: undefined as any,
|
|
98
97
|
}))
|
|
99
98
|
.actions(self => ({
|
|
100
99
|
/**
|
|
@@ -125,8 +124,12 @@ export default function stateModelFactory(
|
|
|
125
124
|
* #action
|
|
126
125
|
*/
|
|
127
126
|
setSamples({ samples, tree }: { samples: Sample[]; tree: unknown }) {
|
|
128
|
-
self.volatileSamples
|
|
129
|
-
|
|
127
|
+
if (!deepEqual(samples, self.volatileSamples)) {
|
|
128
|
+
self.volatileSamples = samples
|
|
129
|
+
}
|
|
130
|
+
if (!deepEqual(tree, self.volatileTree)) {
|
|
131
|
+
self.volatileTree = tree
|
|
132
|
+
}
|
|
130
133
|
},
|
|
131
134
|
}))
|
|
132
135
|
.views(self => ({
|
|
@@ -159,8 +162,8 @@ export default function stateModelFactory(
|
|
|
159
162
|
* #getter
|
|
160
163
|
*/
|
|
161
164
|
get root() {
|
|
162
|
-
return self.
|
|
163
|
-
? hierarchy(self.
|
|
165
|
+
return self.volatileTree
|
|
166
|
+
? hierarchy(self.volatileTree, d => d.children)
|
|
164
167
|
// todo: investigate whether needed, typescript says children always true
|
|
165
168
|
.sum(d => (d.children ? 0 : 1))
|
|
166
169
|
.sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
|
|
@@ -196,7 +199,7 @@ export default function stateModelFactory(
|
|
|
196
199
|
* #getter
|
|
197
200
|
*/
|
|
198
201
|
get totalHeight() {
|
|
199
|
-
return this.samples.length * self.rowHeight
|
|
202
|
+
return this.samples ? this.samples.length * self.rowHeight : 1
|
|
200
203
|
},
|
|
201
204
|
/**
|
|
202
205
|
* #getter
|
|
@@ -237,8 +240,11 @@ export default function stateModelFactory(
|
|
|
237
240
|
rowProportion,
|
|
238
241
|
mismatchRendering,
|
|
239
242
|
} = self
|
|
243
|
+
const s = superRenderProps()
|
|
240
244
|
return {
|
|
241
|
-
...
|
|
245
|
+
...s,
|
|
246
|
+
notReady:
|
|
247
|
+
!self.volatileSamples || !self.volatileTree || super.notReady,
|
|
242
248
|
config: rendererConfig,
|
|
243
249
|
samples,
|
|
244
250
|
rowHeight,
|