jbrowse-plugin-mafviewer 1.0.3 → 1.0.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/CHANGELOG.md +3 -0
- package/README.md +183 -0
- package/dist/jbrowse-plugin-mafviewer.umd.development.js +78 -78
- package/dist/jbrowse-plugin-mafviewer.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +1 -1
- package/package.json +8 -10
- package/src/BigMafAdapter/BigMafAdapter.ts +8 -18
- package/src/LinearMafDisplay/components/ColorLegend.tsx +5 -3
- package/src/LinearMafDisplay/components/ReactComponent.tsx +4 -1
- package/src/LinearMafDisplay/components/SetRowHeight.tsx +2 -2
- package/src/LinearMafDisplay/components/YScaleBars.tsx +6 -2
- package/src/LinearMafDisplay/renderSvg.tsx +2 -2
- package/src/LinearMafDisplay/stateModel.ts +35 -10
- package/src/LinearMafRenderer/LinearMafRenderer.ts +33 -28
- package/src/LinearMafRenderer/components/ReactComponent.tsx +4 -1
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +1 -0
- package/src/MafTabixAdapter/MafTabixAdapter.ts +2 -13
|
@@ -48,6 +48,10 @@ export default function stateModelFactory(
|
|
|
48
48
|
* #property
|
|
49
49
|
*/
|
|
50
50
|
rowProportion: 0.8,
|
|
51
|
+
/**
|
|
52
|
+
* #property
|
|
53
|
+
*/
|
|
54
|
+
showAllLetters: false,
|
|
51
55
|
}),
|
|
52
56
|
)
|
|
53
57
|
.volatile(() => ({
|
|
@@ -66,6 +70,12 @@ export default function stateModelFactory(
|
|
|
66
70
|
setRowProportion(n: number) {
|
|
67
71
|
self.rowProportion = n
|
|
68
72
|
},
|
|
73
|
+
/**
|
|
74
|
+
* #action
|
|
75
|
+
*/
|
|
76
|
+
setShowAllLetters(f: boolean) {
|
|
77
|
+
self.showAllLetters = f
|
|
78
|
+
},
|
|
69
79
|
}))
|
|
70
80
|
.views(self => ({
|
|
71
81
|
/**
|
|
@@ -75,11 +85,9 @@ export default function stateModelFactory(
|
|
|
75
85
|
const r = self.adapterConfig.samples as
|
|
76
86
|
| string[]
|
|
77
87
|
| { id: string; label: string; color?: string }[]
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return r
|
|
82
|
-
}
|
|
88
|
+
return isStrs(r)
|
|
89
|
+
? r.map(elt => ({ id: elt, label: elt, color: undefined }))
|
|
90
|
+
: r
|
|
83
91
|
},
|
|
84
92
|
|
|
85
93
|
/**
|
|
@@ -114,12 +122,20 @@ export default function stateModelFactory(
|
|
|
114
122
|
* #method
|
|
115
123
|
*/
|
|
116
124
|
renderProps() {
|
|
125
|
+
const {
|
|
126
|
+
showAllLetters,
|
|
127
|
+
rendererConfig,
|
|
128
|
+
samples,
|
|
129
|
+
rowHeight,
|
|
130
|
+
rowProportion,
|
|
131
|
+
} = self
|
|
117
132
|
return {
|
|
118
133
|
...superRenderProps(),
|
|
119
|
-
config:
|
|
120
|
-
samples
|
|
121
|
-
rowHeight
|
|
122
|
-
rowProportion
|
|
134
|
+
config: rendererConfig,
|
|
135
|
+
samples,
|
|
136
|
+
rowHeight,
|
|
137
|
+
rowProportion,
|
|
138
|
+
showAllLetters,
|
|
123
139
|
}
|
|
124
140
|
},
|
|
125
141
|
/**
|
|
@@ -137,17 +153,26 @@ export default function stateModelFactory(
|
|
|
137
153
|
])
|
|
138
154
|
},
|
|
139
155
|
},
|
|
156
|
+
{
|
|
157
|
+
label: 'Show all letters',
|
|
158
|
+
type: 'checkbox',
|
|
159
|
+
checked: self.showAllLetters,
|
|
160
|
+
onClick: () => {
|
|
161
|
+
self.setShowAllLetters(!self.showAllLetters)
|
|
162
|
+
},
|
|
163
|
+
},
|
|
140
164
|
]
|
|
141
165
|
},
|
|
142
166
|
}
|
|
143
167
|
})
|
|
144
168
|
.actions(self => {
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
145
170
|
const { renderSvg: superRenderSvg } = self
|
|
146
171
|
return {
|
|
147
172
|
/**
|
|
148
173
|
* #action
|
|
149
174
|
*/
|
|
150
|
-
async renderSvg(opts: ExportSvgDisplayOptions)
|
|
175
|
+
async renderSvg(opts: ExportSvgDisplayOptions) {
|
|
151
176
|
const { renderSvg } = await import('./renderSvg')
|
|
152
177
|
return renderSvg(self, opts, superRenderSvg)
|
|
153
178
|
},
|
|
@@ -36,27 +36,30 @@ function makeImageData({
|
|
|
36
36
|
samples: { id: string; color?: string }[]
|
|
37
37
|
rowHeight: number
|
|
38
38
|
rowProportion: number
|
|
39
|
+
showAllLetters: boolean
|
|
39
40
|
}
|
|
40
41
|
}) {
|
|
41
42
|
const {
|
|
42
43
|
regions,
|
|
43
44
|
bpPerPx,
|
|
44
45
|
rowHeight,
|
|
46
|
+
showAllLetters,
|
|
45
47
|
theme: configTheme,
|
|
46
48
|
samples,
|
|
47
49
|
rowProportion,
|
|
48
50
|
} = renderArgs
|
|
49
51
|
const [region] = regions
|
|
50
52
|
const features = renderArgs.features as Map<string, Feature>
|
|
51
|
-
const h = rowHeight
|
|
53
|
+
const h = rowHeight * rowProportion
|
|
52
54
|
const theme = createJBrowseTheme(configTheme)
|
|
53
55
|
const colorForBase = getColorBaseMap(theme)
|
|
54
56
|
const contrastForBase = getContrastBaseMap(theme)
|
|
55
57
|
const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]))
|
|
56
58
|
const scale = 1 / bpPerPx
|
|
57
59
|
const f = 0.4
|
|
58
|
-
const h2 =
|
|
59
|
-
const
|
|
60
|
+
const h2 = rowHeight / 2
|
|
61
|
+
const hp2 = h / 2
|
|
62
|
+
const offset = (rowHeight - h) / 2
|
|
60
63
|
|
|
61
64
|
// sample as alignments
|
|
62
65
|
ctx.font = 'bold 10px Courier New,monospace'
|
|
@@ -74,7 +77,7 @@ function makeImageData({
|
|
|
74
77
|
throw new Error(`unknown sample encountered: ${sample}`)
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
const t =
|
|
80
|
+
const t = rowHeight * row
|
|
78
81
|
|
|
79
82
|
// gaps
|
|
80
83
|
ctx.beginPath()
|
|
@@ -91,30 +94,32 @@ function makeImageData({
|
|
|
91
94
|
}
|
|
92
95
|
ctx.stroke()
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
if (!showAllLetters) {
|
|
98
|
+
// matches
|
|
99
|
+
ctx.beginPath()
|
|
100
|
+
ctx.fillStyle = 'lightgrey'
|
|
101
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
102
|
+
if (seq[i] !== '-') {
|
|
103
|
+
const c = alignment[i]
|
|
104
|
+
const l = leftPx + scale * o
|
|
105
|
+
if (seq[i] === c && c !== '-') {
|
|
106
|
+
ctx.rect(l, offset + t, scale + f, h)
|
|
107
|
+
}
|
|
108
|
+
o++
|
|
103
109
|
}
|
|
104
|
-
o++
|
|
105
110
|
}
|
|
111
|
+
ctx.fill()
|
|
106
112
|
}
|
|
107
|
-
ctx.fill()
|
|
108
113
|
|
|
109
114
|
// mismatches
|
|
110
115
|
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
111
116
|
const c = alignment[i]
|
|
112
117
|
if (seq[i] !== '-') {
|
|
113
|
-
if (seq[i] !== c && c !== '-') {
|
|
118
|
+
if ((showAllLetters || seq[i] !== c) && c !== '-') {
|
|
114
119
|
const l = leftPx + scale * o
|
|
115
120
|
ctx.fillStyle =
|
|
116
|
-
colorForBase[c as keyof typeof colorForBase] ?? '
|
|
117
|
-
ctx.fillRect(l, offset + t, scale + f,
|
|
121
|
+
colorForBase[c as keyof typeof colorForBase] ?? 'black'
|
|
122
|
+
ctx.fillRect(l, offset + t, scale + f, h)
|
|
118
123
|
}
|
|
119
124
|
o++
|
|
120
125
|
}
|
|
@@ -128,9 +133,9 @@ function makeImageData({
|
|
|
128
133
|
const l = leftPx + scale * o
|
|
129
134
|
const offset = (scale - charSize.w) / 2 + 1
|
|
130
135
|
const c = alignment[i]
|
|
131
|
-
if (seq[i] !== c && c !== '-') {
|
|
132
|
-
ctx.fillStyle = contrastForBase[c] ?? '
|
|
133
|
-
ctx.fillText(origAlignment[i], l + offset,
|
|
136
|
+
if ((showAllLetters || seq[i] !== c) && c !== '-') {
|
|
137
|
+
ctx.fillStyle = contrastForBase[c] ?? 'white'
|
|
138
|
+
ctx.fillText(origAlignment[i], l + offset, hp2 + t + 3)
|
|
134
139
|
}
|
|
135
140
|
o++
|
|
136
141
|
}
|
|
@@ -154,7 +159,7 @@ function makeImageData({
|
|
|
154
159
|
throw new Error(`unknown sample encountered: ${sample}`)
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
const t =
|
|
162
|
+
const t = rowHeight * row
|
|
158
163
|
|
|
159
164
|
ctx.beginPath()
|
|
160
165
|
ctx.fillStyle = 'purple'
|
|
@@ -166,11 +171,11 @@ function makeImageData({
|
|
|
166
171
|
}
|
|
167
172
|
i++
|
|
168
173
|
}
|
|
169
|
-
if (ins.length) {
|
|
170
|
-
const l = leftPx + scale * o -
|
|
171
|
-
ctx.rect(l, offset + t,
|
|
172
|
-
ctx.rect(l - 2, offset + t,
|
|
173
|
-
ctx.rect(l - 2, offset + t +
|
|
174
|
+
if (ins.length > 0) {
|
|
175
|
+
const l = leftPx + scale * o - 1
|
|
176
|
+
ctx.rect(l, offset + t + 1, 1, h - 1)
|
|
177
|
+
ctx.rect(l - 2, offset + t, 5, 1)
|
|
178
|
+
ctx.rect(l - 2, offset + t + h - 1, 5, 1)
|
|
174
179
|
}
|
|
175
180
|
o++
|
|
176
181
|
}
|
|
@@ -199,7 +204,7 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
199
204
|
) {
|
|
200
205
|
const { regions, bpPerPx, samples, rowHeight } = renderProps
|
|
201
206
|
const [region] = regions
|
|
202
|
-
const height = samples.length * rowHeight
|
|
207
|
+
const height = samples.length * rowHeight + 100
|
|
203
208
|
const width = (region.end - region.start) / bpPerPx
|
|
204
209
|
const features = await this.getFeatures(renderProps)
|
|
205
210
|
const res = await renderToAbstractCanvas(width, height, renderProps, ctx =>
|
|
@@ -2,7 +2,10 @@ import { PrerenderedCanvas } from '@jbrowse/core/ui'
|
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import React from 'react'
|
|
4
4
|
|
|
5
|
-
const LinearMafRendering = observer(function (props:
|
|
5
|
+
const LinearMafRendering = observer(function (props: {
|
|
6
|
+
width: number
|
|
7
|
+
height: number
|
|
8
|
+
}) {
|
|
6
9
|
return <PrerenderedCanvas {...props} />
|
|
7
10
|
})
|
|
8
11
|
|
|
@@ -43,6 +43,7 @@ export default function MultiMAFWidget({ model }: { model: AddTrackModel }) {
|
|
|
43
43
|
const [error, setError] = useState<unknown>()
|
|
44
44
|
const [trackName, setTrackName] = useState('MAF track')
|
|
45
45
|
const [choice, setChoice] = useState('BigMafAdapter')
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
47
|
const rootModel = getRoot<any>(model)
|
|
47
48
|
return (
|
|
48
49
|
<Paper className={classes.paper}>
|
|
@@ -58,19 +58,8 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
58
58
|
const data = (feature.get('field5') as string).split(',')
|
|
59
59
|
const alignments = {} as Record<string, OrganismRecord>
|
|
60
60
|
const alns = data.map(elt => elt.split(':')[5])
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// remove extraneous data in other alignments
|
|
64
|
-
// reason being: cannot represent missing data in main species that are in others)
|
|
65
|
-
// for (let i = 0; i < aln.length; i++) {
|
|
66
|
-
// if (aln[i] !== '-') {
|
|
67
|
-
// for (let j = 0; j < data.length; j++) {
|
|
68
|
-
// alns2[j] += alns[j][i]
|
|
69
|
-
// }
|
|
70
|
-
// }
|
|
71
|
-
// }
|
|
72
|
-
for (let j = 0; j < data.length; j++) {
|
|
73
|
-
const elt = data[j]
|
|
61
|
+
|
|
62
|
+
for (const [j, elt] of data.entries()) {
|
|
74
63
|
const ad = elt.split(':')
|
|
75
64
|
const [org, chr] = ad[0].split('.')
|
|
76
65
|
|