@apollo-annotation/jbrowse-plugin-apollo 0.3.1 → 0.3.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/index.esm.js +2072 -1496
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +2069 -1493
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +2256 -1533
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +13 -11
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +7 -10
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +3 -0
- package/src/FeatureDetailsWidget/Attributes.tsx +27 -27
- package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +65 -0
- package/src/FeatureDetailsWidget/TranscriptBasic.tsx +6 -1
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +25 -2
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +0 -1
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +8 -1
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +88 -40
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +8 -1
- package/src/LinearApolloDisplay/stateModel/base.ts +28 -2
- package/src/LinearApolloDisplay/stateModel/layouts.ts +65 -11
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +25 -6
- package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -2
- package/src/OntologyManager/OntologyStore/index.ts +6 -2
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +41 -13
- package/src/OntologyManager/index.ts +35 -0
- package/src/SixFrameFeatureDisplay/stateModel.ts +11 -2
- package/src/TabularEditor/HybridGrid/Feature.tsx +1 -2
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +0 -1
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +8 -1
- package/src/components/AddRefSeqAliases.tsx +7 -8
- package/src/components/CopyFeature.tsx +1 -1
- package/src/components/CreateApolloAnnotation.tsx +304 -0
- package/src/components/DownloadGFF3.tsx +5 -1
- package/src/components/FilterFeatures.tsx +120 -0
- package/src/components/ModifyFeatureAttribute.tsx +27 -27
- package/src/components/OntologyTermMultiSelect.tsx +5 -5
- package/src/extensions/annotationFromJBrowseFeature.test.ts +119 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +171 -0
- package/src/extensions/annotationFromPileup.ts +1 -1
- package/src/extensions/index.ts +1 -0
- package/src/index.ts +8 -2
- package/src/session/ClientDataStore.ts +29 -0
- package/src/session/session.ts +2 -5
- package/src/LinearApolloDisplay/stateModel/getGlyph.ts +0 -40
|
@@ -12,6 +12,7 @@ import { CanvasMouseEvent } from '../types'
|
|
|
12
12
|
import { Glyph } from './Glyph'
|
|
13
13
|
import { boxGlyph } from './BoxGlyph'
|
|
14
14
|
import { LinearApolloDisplayRendering } from '../stateModel/rendering'
|
|
15
|
+
import { OntologyRecord } from '../../OntologyManager'
|
|
15
16
|
|
|
16
17
|
let forwardFillLight: CanvasPattern | null = null
|
|
17
18
|
let backwardFillLight: CanvasPattern | null = null
|
|
@@ -80,6 +81,11 @@ function draw(
|
|
|
80
81
|
return
|
|
81
82
|
}
|
|
82
83
|
const { apolloSelectedFeature } = session
|
|
84
|
+
const { apolloDataStore } = session
|
|
85
|
+
const { featureTypeOntology } = apolloDataStore.ontologyManager
|
|
86
|
+
if (!featureTypeOntology) {
|
|
87
|
+
throw new Error('featureTypeOntology is undefined')
|
|
88
|
+
}
|
|
83
89
|
|
|
84
90
|
// Draw background for gene
|
|
85
91
|
const topLevelFeatureMinX =
|
|
@@ -93,7 +99,8 @@ function draw(
|
|
|
93
99
|
? topLevelFeatureMinX - topLevelFeatureWidthPx
|
|
94
100
|
: topLevelFeatureMinX
|
|
95
101
|
const topLevelFeatureTop = row * rowHeight
|
|
96
|
-
const topLevelFeatureHeight =
|
|
102
|
+
const topLevelFeatureHeight =
|
|
103
|
+
getRowCount(feature, featureTypeOntology) * rowHeight
|
|
97
104
|
|
|
98
105
|
ctx.fillStyle = alpha(theme?.palette.background.paper ?? '#ffffff', 0.6)
|
|
99
106
|
ctx.fillRect(
|
|
@@ -103,19 +110,23 @@ function draw(
|
|
|
103
110
|
topLevelFeatureHeight,
|
|
104
111
|
)
|
|
105
112
|
|
|
106
|
-
// Draw lines on different rows for each
|
|
113
|
+
// Draw lines on different rows for each transcript
|
|
107
114
|
let currentRow = 0
|
|
108
|
-
for (const [,
|
|
109
|
-
|
|
115
|
+
for (const [, transcript] of children) {
|
|
116
|
+
const isTranscript = featureTypeOntology.isTypeOf(
|
|
117
|
+
transcript.type,
|
|
118
|
+
'transcript',
|
|
119
|
+
)
|
|
120
|
+
if (!isTranscript) {
|
|
110
121
|
currentRow += 1
|
|
111
122
|
continue
|
|
112
123
|
}
|
|
113
|
-
const { children:
|
|
114
|
-
if (!
|
|
124
|
+
const { children: childrenOfTranscript, min } = transcript
|
|
125
|
+
if (!childrenOfTranscript) {
|
|
115
126
|
continue
|
|
116
127
|
}
|
|
117
|
-
for (const [, cds] of
|
|
118
|
-
if (cds.type
|
|
128
|
+
for (const [, cds] of childrenOfTranscript) {
|
|
129
|
+
if (!featureTypeOntology.isTypeOf(cds.type, 'CDS')) {
|
|
119
130
|
continue
|
|
120
131
|
}
|
|
121
132
|
const minX =
|
|
@@ -124,7 +135,7 @@ function draw(
|
|
|
124
135
|
coord: min,
|
|
125
136
|
regionNumber: displayedRegionIndex,
|
|
126
137
|
})?.offsetPx ?? 0) - offsetPx
|
|
127
|
-
const widthPx =
|
|
138
|
+
const widthPx = transcript.length / bpPerPx
|
|
128
139
|
const startPx = reversed ? minX - widthPx : minX
|
|
129
140
|
const height =
|
|
130
141
|
Math.round((currentRow + 1 / 2) * rowHeight) + row * rowHeight
|
|
@@ -141,21 +152,21 @@ function draw(
|
|
|
141
152
|
theme?.palette.mode === 'dark' ? forwardFillDark : forwardFillLight
|
|
142
153
|
const backwardFill =
|
|
143
154
|
theme?.palette.mode === 'dark' ? backwardFillDark : backwardFillLight
|
|
144
|
-
// Draw exon and CDS for each
|
|
155
|
+
// Draw exon and CDS for each transcript
|
|
145
156
|
currentRow = 0
|
|
146
157
|
for (const [, child] of children) {
|
|
147
|
-
if (child.type
|
|
158
|
+
if (!featureTypeOntology.isTypeOf(child.type, 'transcript')) {
|
|
148
159
|
boxGlyph.draw(ctx, child, row, stateModel, displayedRegionIndex)
|
|
149
160
|
currentRow += 1
|
|
150
161
|
continue
|
|
151
162
|
}
|
|
152
163
|
for (const cdsRow of child.cdsLocations) {
|
|
153
|
-
const { _id, children:
|
|
154
|
-
if (!
|
|
164
|
+
const { _id, children: childrenOfTranscript } = child
|
|
165
|
+
if (!childrenOfTranscript) {
|
|
155
166
|
continue
|
|
156
167
|
}
|
|
157
|
-
for (const [, exon] of
|
|
158
|
-
if (exon.type
|
|
168
|
+
for (const [, exon] of childrenOfTranscript) {
|
|
169
|
+
if (!featureTypeOntology.isTypeOf(exon.type, 'exon')) {
|
|
159
170
|
continue
|
|
160
171
|
}
|
|
161
172
|
const minX =
|
|
@@ -296,7 +307,9 @@ function drawHover(
|
|
|
296
307
|
stateModel: LinearApolloDisplay,
|
|
297
308
|
ctx: CanvasRenderingContext2D,
|
|
298
309
|
) {
|
|
299
|
-
const { apolloHover, apolloRowHeight, lgv, theme } = stateModel
|
|
310
|
+
const { apolloHover, apolloRowHeight, lgv, session, theme } = stateModel
|
|
311
|
+
const { featureTypeOntology } = session.apolloDataStore.ontologyManager
|
|
312
|
+
|
|
300
313
|
if (!apolloHover) {
|
|
301
314
|
return
|
|
302
315
|
}
|
|
@@ -320,16 +333,26 @@ function drawHover(
|
|
|
320
333
|
const top = row * apolloRowHeight
|
|
321
334
|
const widthPx = length / bpPerPx
|
|
322
335
|
ctx.fillStyle = theme?.palette.action.selected ?? 'rgba(0,0,0,04)'
|
|
323
|
-
|
|
336
|
+
|
|
337
|
+
if (!featureTypeOntology) {
|
|
338
|
+
throw new Error('featureTypeOntology is undefined')
|
|
339
|
+
}
|
|
340
|
+
ctx.fillRect(
|
|
341
|
+
startPx,
|
|
342
|
+
top,
|
|
343
|
+
widthPx,
|
|
344
|
+
apolloRowHeight * getRowCount(feature, featureTypeOntology),
|
|
345
|
+
)
|
|
324
346
|
}
|
|
325
347
|
|
|
326
348
|
function getFeatureFromLayout(
|
|
327
349
|
feature: AnnotationFeature,
|
|
328
350
|
bp: number,
|
|
329
351
|
row: number,
|
|
352
|
+
featureTypeOntology: OntologyRecord,
|
|
330
353
|
): AnnotationFeature | undefined {
|
|
331
354
|
const featureInThisRow: AnnotationFeature[] =
|
|
332
|
-
featuresForRow(feature)[row] || []
|
|
355
|
+
featuresForRow(feature, featureTypeOntology)[row] || []
|
|
333
356
|
for (const f of featureInThisRow) {
|
|
334
357
|
let featureObj
|
|
335
358
|
if (bp >= f.min && bp <= f.max && f.parent) {
|
|
@@ -339,9 +362,9 @@ function getFeatureFromLayout(
|
|
|
339
362
|
continue
|
|
340
363
|
}
|
|
341
364
|
if (
|
|
342
|
-
featureObj.type
|
|
365
|
+
featureTypeOntology.isTypeOf(featureObj.type, 'CDS') &&
|
|
343
366
|
featureObj.parent &&
|
|
344
|
-
featureObj.parent.type
|
|
367
|
+
featureTypeOntology.isTypeOf(featureObj.parent.type, 'transcript')
|
|
345
368
|
) {
|
|
346
369
|
const { cdsLocations } = featureObj.parent
|
|
347
370
|
for (const cdsLoc of cdsLocations) {
|
|
@@ -352,7 +375,7 @@ function getFeatureFromLayout(
|
|
|
352
375
|
}
|
|
353
376
|
}
|
|
354
377
|
|
|
355
|
-
// If mouse position is in the intron region, return the
|
|
378
|
+
// If mouse position is in the intron region, return the transcript
|
|
356
379
|
return featureObj.parent
|
|
357
380
|
}
|
|
358
381
|
// If mouse position is in a feature that is not a CDS, return the feature
|
|
@@ -361,22 +384,28 @@ function getFeatureFromLayout(
|
|
|
361
384
|
return feature
|
|
362
385
|
}
|
|
363
386
|
|
|
364
|
-
function getRowCount(
|
|
387
|
+
function getRowCount(
|
|
388
|
+
feature: AnnotationFeature,
|
|
389
|
+
featureTypeOntology: OntologyRecord,
|
|
390
|
+
_bpPerPx?: number,
|
|
391
|
+
): number {
|
|
365
392
|
const { children, type } = feature
|
|
366
393
|
if (!children) {
|
|
367
394
|
return 1
|
|
368
395
|
}
|
|
396
|
+
const isTranscript = featureTypeOntology.isTypeOf(type, 'transcript')
|
|
369
397
|
let rowCount = 0
|
|
370
|
-
if (
|
|
398
|
+
if (isTranscript) {
|
|
371
399
|
for (const [, child] of children) {
|
|
372
|
-
|
|
400
|
+
const isCds = featureTypeOntology.isTypeOf(child.type, 'CDS')
|
|
401
|
+
if (isCds) {
|
|
373
402
|
rowCount += 1
|
|
374
403
|
}
|
|
375
404
|
}
|
|
376
405
|
return rowCount
|
|
377
406
|
}
|
|
378
407
|
for (const [, child] of children) {
|
|
379
|
-
rowCount += getRowCount(child)
|
|
408
|
+
rowCount += getRowCount(child, featureTypeOntology)
|
|
380
409
|
}
|
|
381
410
|
return rowCount
|
|
382
411
|
}
|
|
@@ -384,11 +413,15 @@ function getRowCount(feature: AnnotationFeature, _bpPerPx?: number): number {
|
|
|
384
413
|
/**
|
|
385
414
|
* A list of all the subfeatures for each row for a given feature, as well as
|
|
386
415
|
* the feature itself.
|
|
387
|
-
* If the row contains
|
|
388
|
-
* If the row does not contain an
|
|
416
|
+
* If the row contains a transcript, the order is CDS -\> exon -\> transcript -\> gene
|
|
417
|
+
* If the row does not contain an transcript, the order is subfeature -\> gene
|
|
389
418
|
*/
|
|
390
|
-
function featuresForRow(
|
|
391
|
-
|
|
419
|
+
function featuresForRow(
|
|
420
|
+
feature: AnnotationFeature,
|
|
421
|
+
featureTypeOntology: OntologyRecord,
|
|
422
|
+
): AnnotationFeature[][] {
|
|
423
|
+
const isGene = featureTypeOntology.isTypeOf(feature.type, 'gene')
|
|
424
|
+
if (!isGene) {
|
|
392
425
|
throw new Error('Top level feature for GeneGlyph must have type "gene"')
|
|
393
426
|
}
|
|
394
427
|
const { children } = feature
|
|
@@ -397,7 +430,7 @@ function featuresForRow(feature: AnnotationFeature): AnnotationFeature[][] {
|
|
|
397
430
|
}
|
|
398
431
|
const features: AnnotationFeature[][] = []
|
|
399
432
|
for (const [, child] of children) {
|
|
400
|
-
if (child.type
|
|
433
|
+
if (!featureTypeOntology.isTypeOf(child.type, 'transcript')) {
|
|
401
434
|
features.push([child, feature])
|
|
402
435
|
continue
|
|
403
436
|
}
|
|
@@ -407,9 +440,9 @@ function featuresForRow(feature: AnnotationFeature): AnnotationFeature[][] {
|
|
|
407
440
|
const cdss: AnnotationFeature[] = []
|
|
408
441
|
const exons: AnnotationFeature[] = []
|
|
409
442
|
for (const [, grandchild] of child.children) {
|
|
410
|
-
if (grandchild.type
|
|
443
|
+
if (featureTypeOntology.isTypeOf(grandchild.type, 'CDS')) {
|
|
411
444
|
cdss.push(grandchild)
|
|
412
|
-
} else if (grandchild.type
|
|
445
|
+
} else if (featureTypeOntology.isTypeOf(grandchild.type, 'exon')) {
|
|
413
446
|
exons.push(grandchild)
|
|
414
447
|
}
|
|
415
448
|
}
|
|
@@ -423,8 +456,9 @@ function featuresForRow(feature: AnnotationFeature): AnnotationFeature[][] {
|
|
|
423
456
|
function getRowForFeature(
|
|
424
457
|
feature: AnnotationFeature,
|
|
425
458
|
childFeature: AnnotationFeature,
|
|
459
|
+
featureTypeOntology: OntologyRecord,
|
|
426
460
|
) {
|
|
427
|
-
const rows = featuresForRow(feature)
|
|
461
|
+
const rows = featuresForRow(feature, featureTypeOntology)
|
|
428
462
|
for (const [idx, row] of rows.entries()) {
|
|
429
463
|
if (row.some((feature) => feature._id === childFeature._id)) {
|
|
430
464
|
return idx
|
|
@@ -496,7 +530,16 @@ function getDraggableFeatureInfo(
|
|
|
496
530
|
feature: AnnotationFeature,
|
|
497
531
|
stateModel: LinearApolloDisplay,
|
|
498
532
|
): { feature: AnnotationFeature; edge: 'min' | 'max' } | undefined {
|
|
499
|
-
|
|
533
|
+
const { session } = stateModel
|
|
534
|
+
const { apolloDataStore } = session
|
|
535
|
+
const { featureTypeOntology } = apolloDataStore.ontologyManager
|
|
536
|
+
if (!featureTypeOntology) {
|
|
537
|
+
throw new Error('featureTypeOntology is undefined')
|
|
538
|
+
}
|
|
539
|
+
const isGene = featureTypeOntology.isTypeOf(feature.type, 'gene')
|
|
540
|
+
const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript')
|
|
541
|
+
const isCds = featureTypeOntology.isTypeOf(feature.type, 'CDS')
|
|
542
|
+
if (isGene || isTranscript) {
|
|
500
543
|
return
|
|
501
544
|
}
|
|
502
545
|
const { bp, refName, regionNumber, x } = mousePosition
|
|
@@ -519,14 +562,19 @@ function getDraggableFeatureInfo(
|
|
|
519
562
|
if (Math.abs(maxPx - x) < 4) {
|
|
520
563
|
return { feature, edge: 'max' }
|
|
521
564
|
}
|
|
522
|
-
if (
|
|
523
|
-
const
|
|
524
|
-
if (!
|
|
565
|
+
if (isCds) {
|
|
566
|
+
const transcript = feature.parent
|
|
567
|
+
if (!transcript?.children) {
|
|
525
568
|
return
|
|
526
569
|
}
|
|
527
|
-
const exonChildren = [
|
|
528
|
-
|
|
529
|
-
|
|
570
|
+
const exonChildren: AnnotationFeature[] = []
|
|
571
|
+
for (const child of transcript.children.values()) {
|
|
572
|
+
const childIsExon = featureTypeOntology.isTypeOf(child.type, 'exon')
|
|
573
|
+
if (childIsExon) {
|
|
574
|
+
exonChildren.push(child)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
530
578
|
const overlappingExon = exonChildren.find((child) => {
|
|
531
579
|
const [start, end] = intersection2(bp, bp + 1, child.min, child.max)
|
|
532
580
|
return start !== undefined && end !== undefined
|
|
@@ -7,10 +7,15 @@ import {
|
|
|
7
7
|
} from '../stateModel/mouseEvents'
|
|
8
8
|
import { LinearApolloDisplayRendering } from '../stateModel/rendering'
|
|
9
9
|
import { CanvasMouseEvent } from '../types'
|
|
10
|
+
import { OntologyRecord } from '../../OntologyManager'
|
|
10
11
|
|
|
11
12
|
export interface Glyph {
|
|
12
13
|
/** @returns number of layout rows used by this glyph with this feature and zoom level */
|
|
13
|
-
getRowCount(
|
|
14
|
+
getRowCount(
|
|
15
|
+
feature: AnnotationFeature,
|
|
16
|
+
featureTypeOntology: OntologyRecord,
|
|
17
|
+
bpPerPx: number,
|
|
18
|
+
): number
|
|
14
19
|
/** draw the feature's primary rendering on the canvas */
|
|
15
20
|
draw(
|
|
16
21
|
ctx: CanvasRenderingContext2D,
|
|
@@ -24,10 +29,12 @@ export interface Glyph {
|
|
|
24
29
|
feature: AnnotationFeature,
|
|
25
30
|
bp: number,
|
|
26
31
|
row: number,
|
|
32
|
+
featureTypeOntology: OntologyRecord,
|
|
27
33
|
): AnnotationFeature | undefined
|
|
28
34
|
getRowForFeature(
|
|
29
35
|
feature: AnnotationFeature,
|
|
30
36
|
childFeature: AnnotationFeature,
|
|
37
|
+
featureTypeOntology: OntologyRecord,
|
|
31
38
|
): number | undefined
|
|
32
39
|
|
|
33
40
|
drawHover(
|
|
@@ -17,11 +17,12 @@ import { getParentRenderProps } from '@jbrowse/core/util/tracks'
|
|
|
17
17
|
// import type LinearGenomeViewPlugin from '@jbrowse/plugin-linear-genome-view'
|
|
18
18
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
19
19
|
import { autorun } from 'mobx'
|
|
20
|
-
import { addDisposer, getRoot, types } from 'mobx-state-tree'
|
|
20
|
+
import { addDisposer, cast, getRoot, types, getSnapshot } from 'mobx-state-tree'
|
|
21
21
|
|
|
22
22
|
import { ApolloInternetAccountModel } from '../../ApolloInternetAccount/model'
|
|
23
23
|
import { ApolloSessionModel } from '../../session'
|
|
24
24
|
import { ApolloRootModel } from '../../types'
|
|
25
|
+
import { FilterFeatures } from '../../components/FilterFeatures'
|
|
25
26
|
|
|
26
27
|
const minDisplayHeight = 20
|
|
27
28
|
|
|
@@ -42,6 +43,7 @@ export function baseModelFactory(
|
|
|
42
43
|
(n) => n >= minDisplayHeight,
|
|
43
44
|
),
|
|
44
45
|
),
|
|
46
|
+
filteredFeatureTypes: types.array(types.string),
|
|
45
47
|
})
|
|
46
48
|
.views((self) => {
|
|
47
49
|
const { configuration, renderProps: superRenderProps } = self
|
|
@@ -162,9 +164,12 @@ export function baseModelFactory(
|
|
|
162
164
|
self.graphical = true
|
|
163
165
|
self.table = true
|
|
164
166
|
},
|
|
167
|
+
updateFilteredFeatureTypes(types: string[]) {
|
|
168
|
+
self.filteredFeatureTypes = cast(types)
|
|
169
|
+
},
|
|
165
170
|
}))
|
|
166
171
|
.views((self) => {
|
|
167
|
-
const { trackMenuItems: superTrackMenuItems } = self
|
|
172
|
+
const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self
|
|
168
173
|
return {
|
|
169
174
|
trackMenuItems() {
|
|
170
175
|
const { graphical, table } = self
|
|
@@ -200,6 +205,27 @@ export function baseModelFactory(
|
|
|
200
205
|
},
|
|
201
206
|
],
|
|
202
207
|
},
|
|
208
|
+
{
|
|
209
|
+
label: 'Filter features by type',
|
|
210
|
+
onClick: () => {
|
|
211
|
+
const session = self.session as unknown as ApolloSessionModel
|
|
212
|
+
;(self.session as unknown as AbstractSessionModel).queueDialog(
|
|
213
|
+
(doneCallback) => [
|
|
214
|
+
FilterFeatures,
|
|
215
|
+
{
|
|
216
|
+
session,
|
|
217
|
+
handleClose: () => {
|
|
218
|
+
doneCallback()
|
|
219
|
+
},
|
|
220
|
+
featureTypes: getSnapshot(filteredFeatureTypes),
|
|
221
|
+
onUpdate: (types: string[]) => {
|
|
222
|
+
self.updateFilteredFeatureTypes(types)
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
)
|
|
227
|
+
},
|
|
228
|
+
},
|
|
203
229
|
]
|
|
204
230
|
},
|
|
205
231
|
}
|
|
@@ -9,7 +9,7 @@ import { addDisposer, isAlive } from 'mobx-state-tree'
|
|
|
9
9
|
|
|
10
10
|
import { ApolloSessionModel } from '../../session'
|
|
11
11
|
import { baseModelFactory } from './base'
|
|
12
|
-
import {
|
|
12
|
+
import { boxGlyph, geneGlyph, genericChildGlyph } from '../glyphs'
|
|
13
13
|
|
|
14
14
|
export function layoutsModelFactory(
|
|
15
15
|
pluginManager: PluginManager,
|
|
@@ -37,7 +37,9 @@ export function layoutsModelFactory(
|
|
|
37
37
|
if (
|
|
38
38
|
refName !== assembly?.getCanonicalRefName(feature.refSeq) ||
|
|
39
39
|
!doesIntersect2(start, end, feature.min, feature.max) ||
|
|
40
|
-
feature.length > self.featuresMinMaxLimit
|
|
40
|
+
feature.length > self.featuresMinMaxLimit ||
|
|
41
|
+
(self.filteredFeatureTypes.length > 0 &&
|
|
42
|
+
!self.filteredFeatureTypes.includes(feature.type))
|
|
41
43
|
) {
|
|
42
44
|
continue
|
|
43
45
|
}
|
|
@@ -60,6 +62,48 @@ export function layoutsModelFactory(
|
|
|
60
62
|
return
|
|
61
63
|
})
|
|
62
64
|
},
|
|
65
|
+
getGlyph(feature: AnnotationFeature) {
|
|
66
|
+
if (this.looksLikeGene(feature)) {
|
|
67
|
+
return geneGlyph
|
|
68
|
+
}
|
|
69
|
+
if (feature.children?.size) {
|
|
70
|
+
return genericChildGlyph
|
|
71
|
+
}
|
|
72
|
+
return boxGlyph
|
|
73
|
+
},
|
|
74
|
+
looksLikeGene(feature: AnnotationFeature): boolean {
|
|
75
|
+
const { featureTypeOntology } =
|
|
76
|
+
self.session.apolloDataStore.ontologyManager
|
|
77
|
+
if (!featureTypeOntology) {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
const { children } = feature
|
|
81
|
+
if (!children?.size) {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
const isGene = featureTypeOntology.isTypeOf(feature.type, 'gene')
|
|
85
|
+
if (!isGene) {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
for (const [, child] of children) {
|
|
89
|
+
if (featureTypeOntology.isTypeOf(child.type, 'transcript')) {
|
|
90
|
+
const { children: grandChildren } = child
|
|
91
|
+
if (!grandChildren?.size) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
const hasCDS = [...grandChildren.values()].some((grandchild) =>
|
|
95
|
+
featureTypeOntology.isTypeOf(grandchild.type, 'CDS'),
|
|
96
|
+
)
|
|
97
|
+
const hasExon = [...grandChildren.values()].some((grandchild) =>
|
|
98
|
+
featureTypeOntology.isTypeOf(grandchild.type, 'exon'),
|
|
99
|
+
)
|
|
100
|
+
if (hasCDS && hasExon) {
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return false
|
|
106
|
+
},
|
|
63
107
|
}))
|
|
64
108
|
.actions((self) => ({
|
|
65
109
|
addSeenFeature(feature: AnnotationFeature) {
|
|
@@ -90,14 +134,20 @@ export function layoutsModelFactory(
|
|
|
90
134
|
}
|
|
91
135
|
if (
|
|
92
136
|
refName !== assembly?.getCanonicalRefName(feature.refSeq) ||
|
|
93
|
-
!doesIntersect2(start, end, feature.min, feature.max)
|
|
137
|
+
!doesIntersect2(start, end, feature.min, feature.max) ||
|
|
138
|
+
(self.filteredFeatureTypes.length > 0 &&
|
|
139
|
+
!self.filteredFeatureTypes.includes(feature.type))
|
|
94
140
|
) {
|
|
95
141
|
continue
|
|
96
142
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
143
|
+
const { featureTypeOntology } =
|
|
144
|
+
self.session.apolloDataStore.ontologyManager
|
|
145
|
+
if (!featureTypeOntology) {
|
|
146
|
+
throw new Error('featureTypeOntology is undefined')
|
|
147
|
+
}
|
|
148
|
+
const rowCount = self
|
|
149
|
+
.getGlyph(feature)
|
|
150
|
+
.getRowCount(feature, featureTypeOntology, self.lgv.bpPerPx)
|
|
101
151
|
let startingRow = 0
|
|
102
152
|
let placed = false
|
|
103
153
|
while (!placed) {
|
|
@@ -158,6 +208,8 @@ export function layoutsModelFactory(
|
|
|
158
208
|
},
|
|
159
209
|
getFeatureLayoutPosition(feature: AnnotationFeature) {
|
|
160
210
|
const { featureLayouts } = this
|
|
211
|
+
const { featureTypeOntology } =
|
|
212
|
+
self.session.apolloDataStore.ontologyManager
|
|
161
213
|
for (const [idx, layout] of featureLayouts.entries()) {
|
|
162
214
|
for (const [layoutRowNum, layoutRow] of layout) {
|
|
163
215
|
for (const [featureRowNum, layoutFeature] of layoutRow) {
|
|
@@ -174,10 +226,12 @@ export function layoutsModelFactory(
|
|
|
174
226
|
}
|
|
175
227
|
}
|
|
176
228
|
if (layoutFeature.hasDescendant(feature._id)) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
229
|
+
if (!featureTypeOntology) {
|
|
230
|
+
throw new Error('featureTypeOntology is undefined')
|
|
231
|
+
}
|
|
232
|
+
const row = self
|
|
233
|
+
.getGlyph(layoutFeature)
|
|
234
|
+
.getRowForFeature(layoutFeature, feature, featureTypeOntology)
|
|
181
235
|
if (row !== undefined) {
|
|
182
236
|
return {
|
|
183
237
|
layoutIndex: idx,
|
|
@@ -15,7 +15,6 @@ import type { CSSProperties } from 'react'
|
|
|
15
15
|
import { Coord } from '../components'
|
|
16
16
|
import { Glyph } from '../glyphs/Glyph'
|
|
17
17
|
import { CanvasMouseEvent } from '../types'
|
|
18
|
-
import { getGlyph } from './getGlyph'
|
|
19
18
|
import { renderingModelFactory } from './rendering'
|
|
20
19
|
import { Frame, getFrame } from '@jbrowse/core/util'
|
|
21
20
|
|
|
@@ -147,11 +146,17 @@ export function mouseEventsModelIntermediateFactory(
|
|
|
147
146
|
return mousePosition
|
|
148
147
|
}
|
|
149
148
|
const [featureRow, topLevelFeature] = foundFeature
|
|
150
|
-
const glyph = getGlyph(topLevelFeature)
|
|
149
|
+
const glyph = self.getGlyph(topLevelFeature)
|
|
150
|
+
const { featureTypeOntology } =
|
|
151
|
+
self.session.apolloDataStore.ontologyManager
|
|
152
|
+
if (!featureTypeOntology) {
|
|
153
|
+
throw new Error('featureTypeOntology is undefined')
|
|
154
|
+
}
|
|
151
155
|
const feature = glyph.getFeatureFromLayout(
|
|
152
156
|
topLevelFeature,
|
|
153
157
|
bp,
|
|
154
158
|
featureRow,
|
|
159
|
+
featureTypeOntology,
|
|
155
160
|
)
|
|
156
161
|
if (!feature) {
|
|
157
162
|
return mousePosition
|
|
@@ -227,15 +232,27 @@ export function mouseEventsSeqHightlightModelFactory(
|
|
|
227
232
|
self.lgv.bpPerPx <= 1 ? 125 : 95,
|
|
228
233
|
)
|
|
229
234
|
|
|
230
|
-
const {
|
|
235
|
+
const {
|
|
236
|
+
apolloHover,
|
|
237
|
+
lgv,
|
|
238
|
+
regions,
|
|
239
|
+
sequenceRowHeight,
|
|
240
|
+
session,
|
|
241
|
+
theme,
|
|
242
|
+
} = self
|
|
231
243
|
|
|
232
244
|
if (!apolloHover) {
|
|
233
245
|
return
|
|
234
246
|
}
|
|
235
247
|
const { feature } = apolloHover
|
|
236
248
|
|
|
249
|
+
const { featureTypeOntology } =
|
|
250
|
+
session.apolloDataStore.ontologyManager
|
|
251
|
+
if (!featureTypeOntology) {
|
|
252
|
+
throw new Error('featureTypeOntology is undefined')
|
|
253
|
+
}
|
|
237
254
|
for (const [idx, region] of regions.entries()) {
|
|
238
|
-
if (feature.type
|
|
255
|
+
if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
|
|
239
256
|
const parentFeature = feature.parent
|
|
240
257
|
if (!parentFeature) {
|
|
241
258
|
continue
|
|
@@ -323,7 +340,7 @@ export function mouseEventsModelFactory(
|
|
|
323
340
|
return []
|
|
324
341
|
}
|
|
325
342
|
const { topLevelFeature } = apolloHover
|
|
326
|
-
const glyph = getGlyph(topLevelFeature)
|
|
343
|
+
const glyph = self.getGlyph(topLevelFeature)
|
|
327
344
|
return glyph.getContextMenuItems(self)
|
|
328
345
|
},
|
|
329
346
|
}))
|
|
@@ -483,7 +500,9 @@ export function mouseEventsModelFactory(
|
|
|
483
500
|
if (apolloDragging) {
|
|
484
501
|
// NOTE: the glyph where the drag started is responsible for drawing the preview.
|
|
485
502
|
// it can call methods in other glyphs to help with this though.
|
|
486
|
-
const glyph = getGlyph(
|
|
503
|
+
const glyph = self.getGlyph(
|
|
504
|
+
apolloDragging.feature.topLevelFeature,
|
|
505
|
+
)
|
|
487
506
|
glyph.drawDragPreview(self, ctx)
|
|
488
507
|
}
|
|
489
508
|
},
|
|
@@ -7,7 +7,6 @@ import { autorun } from 'mobx'
|
|
|
7
7
|
import { Instance, addDisposer } from 'mobx-state-tree'
|
|
8
8
|
|
|
9
9
|
import { ApolloSessionModel } from '../../session'
|
|
10
|
-
import { getGlyph } from './getGlyph'
|
|
11
10
|
import { layoutsModelFactory } from './layouts'
|
|
12
11
|
|
|
13
12
|
export function renderingModelIntermediateFactory(
|
|
@@ -428,7 +427,7 @@ export function renderingModelFactory(
|
|
|
428
427
|
) {
|
|
429
428
|
continue
|
|
430
429
|
}
|
|
431
|
-
getGlyph(feature).draw(ctx, feature, row, self, idx)
|
|
430
|
+
self.getGlyph(feature).draw(ctx, feature, row, self, idx)
|
|
432
431
|
}
|
|
433
432
|
}
|
|
434
433
|
}
|
|
@@ -68,6 +68,7 @@ export interface OntologyStoreOptions {
|
|
|
68
68
|
indexFields?: { displayName: string; jsonPath: string }[]
|
|
69
69
|
}
|
|
70
70
|
maxSearchResults?: number
|
|
71
|
+
update?(message: string, progress: number): void
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
export interface PropertiesOptions {
|
|
@@ -107,8 +108,8 @@ export default class OntologyStore {
|
|
|
107
108
|
this.ontologyName = name
|
|
108
109
|
this.ontologyVersion = version
|
|
109
110
|
this.sourceLocation = source
|
|
110
|
-
this.db = this.prepareDatabase()
|
|
111
111
|
this.options = options ?? {}
|
|
112
|
+
this.db = this.prepareDatabase()
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
/**
|
|
@@ -174,9 +175,12 @@ export default class OntologyStore {
|
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
try {
|
|
177
|
-
const { sourceLocation, sourceType } = this
|
|
178
|
+
const { options, sourceLocation, sourceType } = this
|
|
178
179
|
if (sourceType === 'obo-graph-json') {
|
|
180
|
+
options.update?.('', 0)
|
|
181
|
+
// add more updates inside `loadOboGraphJson`
|
|
179
182
|
await this.loadOboGraphJson(db)
|
|
183
|
+
options.update?.('', 100)
|
|
180
184
|
} else {
|
|
181
185
|
throw new Error(
|
|
182
186
|
`ontology source file ${JSON.stringify(
|