@jbrowse/plugin-wiggle 1.5.7 → 1.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-wiggle",
3
- "version": "1.5.7",
3
+ "version": "1.6.2",
4
4
  "description": "JBrowse 2 wiggle adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -60,5 +60,5 @@
60
60
  "publishConfig": {
61
61
  "access": "public"
62
62
  },
63
- "gitHead": "b9b69276a6760ca01c7c7d905e2172b4adf9df8d"
63
+ "gitHead": "92455c6021abd69548496a450983d89f8837860d"
64
64
  }
@@ -5,15 +5,15 @@ import {
5
5
  } from '@jbrowse/core/data_adapters/BaseAdapter'
6
6
  import { AugmentedRegion as Region } from '@jbrowse/core/util/types'
7
7
  import { openLocation } from '@jbrowse/core/util/io'
8
+ import { updateStatus } from '@jbrowse/core/util'
8
9
  import { ObservableCreate } from '@jbrowse/core/util/rxjs'
9
10
  import SimpleFeature, { Feature } from '@jbrowse/core/util/simpleFeature'
10
11
  import { map, mergeAll } from 'rxjs/operators'
11
12
  import { readConfObject } from '@jbrowse/core/configuration'
12
- import { Instance } from 'mobx-state-tree'
13
+ import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
13
14
  import { rectifyStats, UnrectifiedFeatureStats } from '@jbrowse/core/util/stats'
14
15
  import PluginManager from '@jbrowse/core/PluginManager'
15
16
 
16
- import configSchema from './configSchema'
17
17
  import { getSubAdapterType } from '@jbrowse/core/data_adapters/dataAdapterCache'
18
18
 
19
19
  interface WiggleOptions extends BaseOptions {
@@ -30,7 +30,7 @@ export default class BigWigAdapter extends BaseFeatureDataAdapter {
30
30
  ]
31
31
 
32
32
  public constructor(
33
- config: Instance<typeof configSchema>,
33
+ config: AnyConfigurationModel,
34
34
  getSubAdapter?: getSubAdapterType,
35
35
  pluginManager?: PluginManager,
36
36
  ) {
@@ -45,25 +45,24 @@ export default class BigWigAdapter extends BaseFeatureDataAdapter {
45
45
 
46
46
  private async setup(opts?: BaseOptions) {
47
47
  const { statusCallback = () => {} } = opts || {}
48
- statusCallback('Downloading bigwig header')
49
- const result = await this.bigwig.getHeader(opts)
50
- statusCallback('')
51
- return result
48
+ return updateStatus('Downloading bigwig header', statusCallback, () =>
49
+ this.bigwig.getHeader(opts),
50
+ )
52
51
  }
53
52
 
54
53
  public async getRefNames(opts?: BaseOptions) {
55
- const header = await this.setup(opts)
56
- return Object.keys(header.refsByName)
54
+ const { refsByName } = await this.setup(opts)
55
+ return Object.keys(refsByName)
57
56
  }
58
57
 
59
58
  public async refIdToName(refId: number) {
60
- const h = await this.setup()
61
- return (h.refsByNumber[refId] || { name: undefined }).name
59
+ const { refsByNumber } = await this.setup()
60
+ return refsByNumber[refId]?.name
62
61
  }
63
62
 
64
63
  public async getGlobalStats(opts?: BaseOptions) {
65
- const header = await this.setup(opts)
66
- return rectifyStats(header.totalSummary as UnrectifiedFeatureStats)
64
+ const { totalSummary } = await this.setup(opts)
65
+ return rectifyStats(totalSummary as UnrectifiedFeatureStats)
67
66
  }
68
67
 
69
68
  public getFeatures(region: Region, opts: WiggleOptions = {}) {
@@ -92,5 +91,10 @@ export default class BigWigAdapter extends BaseFeatureDataAdapter {
92
91
  }, signal)
93
92
  }
94
93
 
94
+ // always render bigwig instead of calculating a feature density for it
95
+ async estimateRegionsStats(_regions: Region[]) {
96
+ return { featureDensity: 0 }
97
+ }
98
+
95
99
  public freeResources(): void {}
96
100
  }
@@ -24,11 +24,10 @@ export default class DensityRenderer extends WiggleBaseRenderer {
24
24
  const pivotValue = readConfObject(config, 'bicolorPivotValue')
25
25
  const negColor = readConfObject(config, 'negColor')
26
26
  const posColor = readConfObject(config, 'posColor')
27
+ const color = readConfObject(config, 'color')
27
28
  let colorCallback
28
- let colorScale: ReturnType<typeof getScale>
29
- if (readConfObject(config, 'color') === '#f0f') {
30
- // default color, use posColor/negColor instead
31
- colorScale =
29
+ if (color === '#f0f') {
30
+ const colorScale =
32
31
  pivot !== 'none'
33
32
  ? getScale({
34
33
  ...scaleOpts,
@@ -1,17 +1,20 @@
1
- import React from 'react'
2
- import { makeStyles } from '@material-ui/core/styles'
1
+ import React, { useState } from 'react'
3
2
  import {
3
+ Button,
4
4
  Dialog,
5
5
  DialogContent,
6
+ DialogActions,
6
7
  DialogTitle,
7
8
  IconButton,
8
- Button,
9
+ Typography,
10
+ FormControlLabel,
11
+ Radio,
12
+ makeStyles,
9
13
  } from '@material-ui/core'
10
14
  import CloseIcon from '@material-ui/icons/Close'
11
15
  import { CompactPicker, Color, RGBColor } from 'react-color'
12
16
 
13
17
  const useStyles = makeStyles(theme => ({
14
- root: {},
15
18
  closeButton: {
16
19
  position: 'absolute',
17
20
  right: theme.spacing(1),
@@ -22,33 +25,34 @@ const useStyles = makeStyles(theme => ({
22
25
 
23
26
  // this is needed because passing a entire color object into the react-color
24
27
  // for alpha, can't pass in an rgba string for example
25
- function serializeColor(color: Color) {
28
+ function serialize(color: Color) {
26
29
  if (color instanceof Object) {
27
- const { r, g, b, a } = color as RGBColor
28
- return `rgb(${r},${g},${b},${a})`
30
+ const { r, g, b } = color as RGBColor
31
+ return `rgb(${r},${g},${b})`
29
32
  }
30
33
  return color
31
34
  }
32
35
 
33
- export default function SetColorDialog(props: {
36
+ export default function SetColorDialog({
37
+ model,
38
+ handleClose,
39
+ }: {
34
40
  model: {
35
41
  color: number
36
- setColor: Function
42
+ setColor: (arg?: string) => void
43
+ setPosColor: (arg?: string) => void
44
+ setNegColor: (arg?: string) => void
37
45
  }
38
46
  handleClose: () => void
39
47
  }) {
40
48
  const classes = useStyles()
41
- const { model, handleClose } = props
49
+ const [posneg, setPosNeg] = useState(false)
42
50
 
43
51
  return (
44
- <Dialog
45
- open
46
- onClose={handleClose}
47
- aria-labelledby="alert-dialog-title"
48
- aria-describedby="alert-dialog-description"
49
- >
50
- <DialogTitle id="alert-dialog-title">
51
- Select a color
52
+ <Dialog open onClose={handleClose}>
53
+ <DialogTitle>
54
+ Select either an overall color, or the positive/negative colors. Note
55
+ that density renderers only work properly with positive/negative colors
52
56
  <IconButton
53
57
  aria-label="close"
54
58
  className={classes.closeButton}
@@ -57,37 +61,72 @@ export default function SetColorDialog(props: {
57
61
  <CloseIcon />
58
62
  </IconButton>
59
63
  </DialogTitle>
60
- <DialogContent style={{ overflowX: 'hidden' }}>
61
- <div className={classes.root}>
62
- <CompactPicker
63
- onChange={event => {
64
- model.setColor(serializeColor(event.rgb))
65
- }}
66
- />
67
- <br />
68
- <div style={{ margin: 20 }}>
69
- <Button
70
- onClick={() => {
64
+ <DialogContent>
65
+ <FormControlLabel
66
+ checked={!posneg}
67
+ onClick={() => setPosNeg(false)}
68
+ control={<Radio />}
69
+ label={'Overall color'}
70
+ />
71
+ <FormControlLabel
72
+ checked={posneg}
73
+ onClick={() => setPosNeg(true)}
74
+ control={<Radio />}
75
+ label={'Positive/negative color'}
76
+ />
77
+
78
+ {posneg ? (
79
+ <>
80
+ <Typography>Positive color</Typography>
81
+ <CompactPicker
82
+ onChange={event => {
83
+ model.setPosColor(serialize(event.rgb))
84
+ model.setColor(undefined)
85
+ }}
86
+ />
87
+ <Typography>Negative color</Typography>
88
+ <CompactPicker
89
+ onChange={event => {
90
+ model.setNegColor(serialize(event.rgb))
71
91
  model.setColor(undefined)
72
92
  }}
73
- color="secondary"
74
- variant="contained"
75
- >
76
- Restore default from config
77
- </Button>
78
- <Button
79
- variant="contained"
80
- color="primary"
81
- type="submit"
82
- onClick={() => {
83
- handleClose()
93
+ />
94
+ </>
95
+ ) : (
96
+ <>
97
+ <Typography>Overall color</Typography>
98
+ <CompactPicker
99
+ onChange={event => {
100
+ model.setColor(serialize(event.rgb))
84
101
  }}
85
- >
86
- Submit
87
- </Button>
88
- </div>
89
- </div>
102
+ />
103
+ </>
104
+ )}
90
105
  </DialogContent>
106
+ <DialogActions>
107
+ <Button
108
+ onClick={() => {
109
+ model.setPosColor(undefined)
110
+ model.setNegColor(undefined)
111
+ model.setColor(undefined)
112
+ }}
113
+ color="secondary"
114
+ variant="contained"
115
+ >
116
+ Restore default
117
+ </Button>
118
+
119
+ <Button
120
+ variant="contained"
121
+ color="primary"
122
+ type="submit"
123
+ onClick={() => {
124
+ handleClose()
125
+ }}
126
+ >
127
+ Submit
128
+ </Button>
129
+ </DialogActions>
91
130
  </Dialog>
92
131
  )
93
132
  }
@@ -26,11 +26,11 @@ export const YScaleBar = observer(
26
26
 
27
27
  const LinearWiggleDisplay = observer((props: { model: WiggleDisplayModel }) => {
28
28
  const { model } = props
29
- const { ready, stats, height, needsScalebar } = model
29
+ const { stats, height, needsScalebar } = model
30
30
  return (
31
31
  <div>
32
32
  <BaseLinearDisplayComponent {...props} />
33
- {ready && stats && needsScalebar ? (
33
+ {stats && needsScalebar ? (
34
34
  <svg
35
35
  style={{
36
36
  position: 'absolute',
@@ -27,6 +27,7 @@ export default function WiggleConfigFactory(pluginManager: PluginManager) {
27
27
  description:
28
28
  'global/local using their min/max values or w/ standard deviations (globalsd/localsd)',
29
29
  },
30
+
30
31
  minScore: {
31
32
  type: 'number',
32
33
  defaultValue: Number.MIN_VALUE,
@@ -65,6 +65,8 @@ const stateModelFactory = (
65
65
  resolution: types.optional(types.number, 1),
66
66
  fill: types.maybe(types.boolean),
67
67
  color: types.maybe(types.string),
68
+ posColor: types.maybe(types.string),
69
+ negColor: types.maybe(types.string),
68
70
  summaryScoreMode: types.maybe(types.string),
69
71
  rendererTypeNameState: types.maybe(types.string),
70
72
  scale: types.maybe(types.string),
@@ -80,7 +82,7 @@ const stateModelFactory = (
80
82
  }),
81
83
  )
82
84
  .volatile(() => ({
83
- ready: false,
85
+ statsReady: false,
84
86
  message: undefined as undefined | string,
85
87
  stats: observable({ scoreMin: 0, scoreMax: 50 }),
86
88
  statsFetchInProgress: undefined as undefined | AbortController,
@@ -89,11 +91,17 @@ const stateModelFactory = (
89
91
  updateStats(stats: { scoreMin: number; scoreMax: number }) {
90
92
  self.stats.scoreMin = stats.scoreMin
91
93
  self.stats.scoreMax = stats.scoreMax
92
- self.ready = true
94
+ self.statsReady = true
93
95
  },
94
96
  setColor(color: string) {
95
97
  self.color = color
96
98
  },
99
+ setPosColor(color: string) {
100
+ self.posColor = color
101
+ },
102
+ setNegColor(color: string) {
103
+ self.negColor = color
104
+ },
97
105
 
98
106
  setLoading(aborter: AbortController) {
99
107
  const { statsFetchInProgress: statsFetch } = self
@@ -161,7 +169,7 @@ const stateModelFactory = (
161
169
  },
162
170
  }))
163
171
  .views(self => ({
164
- get TooltipComponent(): React.FC {
172
+ get TooltipComponent() {
165
173
  return Tooltip as unknown as React.FC
166
174
  },
167
175
 
@@ -187,11 +195,6 @@ const stateModelFactory = (
187
195
  get scaleType() {
188
196
  return self.scale || getConf(self, 'scaleType')
189
197
  },
190
- get filled() {
191
- return typeof self.fill !== 'undefined'
192
- ? self.fill
193
- : readConfObject(this.rendererConfig, 'filled')
194
- },
195
198
 
196
199
  get maxScore() {
197
200
  const { max } = self.constraints
@@ -202,19 +205,22 @@ const stateModelFactory = (
202
205
  const { min } = self.constraints
203
206
  return min !== undefined ? min : getConf(self, 'minScore')
204
207
  },
205
-
208
+ }))
209
+ .views(self => ({
206
210
  get rendererConfig() {
207
211
  const configBlob =
208
- getConf(self, ['renderers', this.rendererTypeName]) || {}
212
+ getConf(self, ['renderers', self.rendererTypeName]) || {}
209
213
 
210
214
  return self.rendererType.configSchema.create(
211
215
  {
212
216
  ...configBlob,
213
217
  filled: self.fill,
214
- scaleType: this.scaleType,
218
+ scaleType: self.scaleType,
215
219
  displayCrossHatches: self.displayCrossHatches,
216
220
  summaryScoreMode: self.summaryScoreMode,
217
- color: self.color,
221
+ ...(self.color ? { color: self.color } : {}),
222
+ ...(self.negColor ? { negColor: self.negColor } : {}),
223
+ ...(self.posColor ? { posColor: self.posColor } : {}),
218
224
  },
219
225
  getEnv(self),
220
226
  )
@@ -223,6 +229,11 @@ const stateModelFactory = (
223
229
  .views(self => {
224
230
  let oldDomain: [number, number] = [0, 0]
225
231
  return {
232
+ get filled() {
233
+ return typeof self.fill !== 'undefined'
234
+ ? self.fill
235
+ : readConfObject(self.rendererConfig, 'filled')
236
+ },
226
237
  get summaryScoreModeSetting() {
227
238
  return (
228
239
  self.summaryScoreMode ||
@@ -311,9 +322,10 @@ const stateModelFactory = (
311
322
  const { renderProps: superRenderProps } = self
312
323
  return {
313
324
  renderProps() {
325
+ const superProps = superRenderProps()
314
326
  return {
315
- ...superRenderProps(),
316
- notReady: !self.ready,
327
+ ...superProps,
328
+ notReady: superProps.notReady || !self.statsReady,
317
329
  rpcDriverName: self.rpcDriverName,
318
330
  displayModel: self,
319
331
  config: self.rendererConfig,
@@ -481,6 +493,7 @@ const stateModelFactory = (
481
493
  },
482
494
  ...opts,
483
495
  }
496
+
484
497
  if (autoscaleType === 'global' || autoscaleType === 'globalsd') {
485
498
  const results: FeatureStats = (await rpcManager.call(
486
499
  sessionId,
@@ -572,17 +585,20 @@ const stateModelFactory = (
572
585
  return
573
586
  }
574
587
 
575
- if (view.bpPerPx > self.maxViewBpPerPx) {
588
+ if (!self.estimatedStatsReady) {
589
+ return
590
+ }
591
+ if (self.regionTooLarge) {
576
592
  return
577
593
  }
578
594
 
579
- const stats = await getStats({
595
+ const wiggleStats = await getStats({
580
596
  signal: aborter.signal,
581
597
  filters: self.filters,
582
598
  })
583
599
 
584
600
  if (isAlive(self)) {
585
- self.updateStats(stats)
601
+ self.updateStats(wiggleStats)
586
602
  }
587
603
  } catch (e) {
588
604
  if (!isAbortException(e) && isAlive(self)) {
@@ -596,7 +612,7 @@ const stateModelFactory = (
596
612
  )
597
613
  },
598
614
  async renderSvg(opts: ExportSvgOpts) {
599
- await when(() => self.ready && !!self.regionCannotBeRenderedText)
615
+ await when(() => self.statsReady && !!self.regionCannotBeRenderedText)
600
616
  const { needsScalebar, stats } = self
601
617
  const { offsetPx } = getContainingView(self) as LGV
602
618
  return (
package/src/index.ts CHANGED
@@ -103,12 +103,17 @@ export default class WigglePlugin extends Plugin {
103
103
  const regexGuess = /\.(bw|bigwig)$/i
104
104
  const adapterName = 'BigWigAdapter'
105
105
  const fileName = getFileName(file)
106
- if (regexGuess.test(fileName) || adapterHint === adapterName) {
107
- return {
108
- type: adapterName,
109
- bigWigLocation: file,
110
- }
106
+ const obj = {
107
+ type: adapterName,
108
+ bigWigLocation: file,
111
109
  }
110
+
111
+ if (regexGuess.test(fileName) && !adapterHint) {
112
+ return obj
113
+ } else if (adapterHint === adapterName) {
114
+ return obj
115
+ }
116
+
112
117
  return adapterGuesser(file, index, adapterHint)
113
118
  }
114
119
  },
package/src/util.ts CHANGED
@@ -41,7 +41,6 @@ export function getScale({
41
41
  throw new Error('undefined scaleType')
42
42
  }
43
43
  scale.domain(pivotValue !== undefined ? [min, pivotValue, max] : [min, max])
44
- // console.log('before', scale.domain())
45
44
  scale.nice()
46
45
 
47
46
  const [rangeMin, rangeMax] = range
@@ -49,8 +48,6 @@ export function getScale({
49
48
  throw new Error('invalid range')
50
49
  }
51
50
  scale.range(inverted ? range.slice().reverse() : range)
52
-
53
- // console.log('after', scale.domain())
54
51
  return scale
55
52
  }
56
53
  /**
@@ -118,7 +115,7 @@ export function getNiceDomain({
118
115
  }
119
116
  }
120
117
  if (min === undefined || max === undefined) {
121
- throw new Error('invalid domain')
118
+ throw new Error('invalid domain supplied to stats function')
122
119
  }
123
120
  if (minScore !== undefined && minScore !== Number.MIN_VALUE) {
124
121
  min = minScore