Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/LinearManhattanDisplay/LinearGWASDisplayComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react'

import { getContainingView, getEnv } from '@jbrowse/core/util'
import { observer } from 'mobx-react'

import type { LinearManhattanDisplayModel } from './model'
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
import type WigglePlugin from '@jbrowse/plugin-wiggle'

const LinearGWASDisplayComponent = observer(function ({
model,
}: {
model: LinearManhattanDisplayModel
}) {
const { pluginManager } = getEnv(model)
const WigglePlugin = pluginManager.getPlugin('WigglePlugin') as WigglePlugin

const { LinearWiggleDisplayReactComponent } = WigglePlugin.exports
const { highScoringFeatures } = model
const view = getContainingView(model) as LinearGenomeViewModel

return (
<div style={{ position: 'relative' }}>
{highScoringFeatures.map(({ feature, y }) => {
const ret = view.bpToPx({
refName: feature.get('refName'),
coord: feature.get('start'),
})
return ret ? (
<div
key={feature.id()}
style={{
position: 'absolute',
left: ret.offsetPx - view.offsetPx,
top: y,
}}
>
{feature.get('name') || feature.get('rsid')}
</div>
) : null
})}
<LinearWiggleDisplayReactComponent model={model} />
</div>
)
})

export default LinearGWASDisplayComponent
10 changes: 3 additions & 7 deletions src/LinearManhattanDisplay/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { DisplayType } from '@jbrowse/core/pluggableElementTypes'

import LinearGWASDisplayComponent from './LinearGWASDisplayComponent'
import { configSchemaFactory } from './configSchemaFactory'
import { stateModelFactory } from './stateModelFactory'
import { stateModelFactory } from './model'

import type PluginManager from '@jbrowse/core/PluginManager'
import type WigglePlugin from '@jbrowse/plugin-wiggle'

export default function LinearManhattanDisplayF(pluginManager: PluginManager) {
const WigglePlugin = pluginManager.getPlugin('WigglePlugin') as WigglePlugin

const { LinearWiggleDisplayReactComponent } = WigglePlugin.exports

pluginManager.addDisplayType(() => {
const configSchema = configSchemaFactory(pluginManager)
return new DisplayType({
Expand All @@ -19,7 +15,7 @@ export default function LinearManhattanDisplayF(pluginManager: PluginManager) {
stateModel: stateModelFactory(pluginManager, configSchema),
trackType: 'FeatureTrack',
viewType: 'LinearGenomeView',
ReactComponent: LinearWiggleDisplayReactComponent,
ReactComponent: LinearGWASDisplayComponent,
})
})
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,75 @@
import {
SimpleFeature,
getContainingTrack,
getContainingView,
getSession,
isSelectionContainer,
isSessionModelWithWidgets,
} from '@jbrowse/core/util'
import { observable } from 'mobx'
import { types } from 'mobx-state-tree'

import TooltipComponent from './TooltipComponent'

import type PluginManager from '@jbrowse/core/PluginManager'
import type { Feature } from '@jbrowse/core/util'
import type { AnyConfigurationSchemaType } from '@jbrowse/core/configuration'
import type { Feature, SimpleFeatureSerialized } from '@jbrowse/core/util'
import type WigglePlugin from '@jbrowse/plugin-wiggle'
import type { Instance } from 'mobx-state-tree'
import type RBush from 'rbush'

type BlockClickMap = RBush<{
feature: SimpleFeatureSerialized
minY: number
}>

export function stateModelFactory(
pluginManager: PluginManager,
configSchema: any,
configSchema: AnyConfigurationSchemaType,
) {
const { types } = pluginManager.lib['mobx-state-tree']
const WigglePlugin = pluginManager.getPlugin('WigglePlugin') as WigglePlugin
const { linearWiggleDisplayModelFactory } = WigglePlugin.exports
return types.compose(
'LinearManhattanDisplay',
linearWiggleDisplayModelFactory(pluginManager, configSchema),
types
.model({
/**
* #property
*/
type: types.literal('LinearManhattanDisplay'),
})
.volatile(() => ({
/**
* #volatile
*/
displayClickMaps: observable.map<string, BlockClickMap>(),
}))
.views(() => ({
/**
* #getter
*/
get TooltipComponent() {
return TooltipComponent
},

/**
* #getter
*/
get rendererTypeName() {
return 'LinearManhattanRenderer'
},

/**
* #getter
*/
get needsScalebar() {
return true
},

/**
* #getter
*/
get regionTooLarge() {
return false
},
Expand Down Expand Up @@ -66,8 +100,43 @@ export function stateModelFactory(
session.setSelection(feature)
}
},
/**
* #action
*/
setBlockClickMap(blockKey: string, clickMap: BlockClickMap) {
self.displayClickMaps.set(blockKey, clickMap)
},
/**
* #action
*/
removeBlockClickMap(blockKey: string) {
self.displayClickMaps.delete(blockKey)
},
}))
.views(self => ({
/**
* #getter
*/
get highScoringFeatures() {
const ret = [] as { feature: Feature; y: number }[]
for (const clickMap of self.displayClickMaps.values()) {
for (const entry of clickMap.all()) {
if (entry.feature.score > 30) {
ret.push({
feature: new SimpleFeature(entry.feature),
y: entry.minY,
})
}
}
}
return ret
},
})),
)
}

export type LinearManhattanDisplayModel = ReturnType<typeof stateModelFactory>
export type LinearManhattanDisplayStateModel = ReturnType<
typeof stateModelFactory
>
export type LinearManhattanDisplayModel =
Instance<LinearManhattanDisplayStateModel>
28 changes: 23 additions & 5 deletions src/LinearManhattanRenderer/LinearManhattanRendering.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useRef } from 'react'
import React, { useEffect, useMemo, useRef } from 'react'

import { PrerenderedCanvas } from '@jbrowse/core/ui'
import { SimpleFeature } from '@jbrowse/core/util'
Expand All @@ -17,16 +17,34 @@ const LinearManhattanRendering = observer(function (props: {
blockKey: string
scaleOpts: any
clickMap: any
displayModel?: any
onMouseLeave?: (event: React.MouseEvent) => void
onMouseMove?: (event: React.MouseEvent, arg?: string) => void
onFeatureClick?: (event: React.MouseEvent, arg?: string) => void
}) {
const { height, onMouseLeave, onMouseMove, onFeatureClick, clickMap } = props
const clickMap2 = useMemo(() => {
return new RBush<{ feature: SimpleFeatureSerialized }>().fromJSON(clickMap)
}, [clickMap])
const {
blockKey,
clickMap,
displayModel,
height,
onMouseLeave,
onMouseMove,
onFeatureClick,
} = props

const clickMap2 = useMemo(
() => new RBush<{ feature: SimpleFeatureSerialized }>().fromJSON(clickMap),
[clickMap],
)
const ref = useRef<HTMLDivElement>(null)

useEffect(() => {
displayModel.setBlockClickMap(blockKey, clickMap2)
return () => {
displayModel.removeBlockClickMap(blockKey)
}
}, [displayModel, blockKey, clickMap2])

function getFeatureUnderMouse(eventClientX: number, eventClientY: number) {
// calculates feature under mouse
let offsetX = 0
Expand Down
Loading