@jbrowse/plugin-alignments 1.5.8 → 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-alignments",
3
- "version": "1.5.8",
3
+ "version": "1.6.2",
4
4
  "description": "JBrowse 2 alignments adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -35,7 +35,7 @@
35
35
  "useSrc": "node ../../scripts/useSrc.js"
36
36
  },
37
37
  "dependencies": {
38
- "@gmod/bam": "^1.1.9",
38
+ "@gmod/bam": "^1.1.11",
39
39
  "@gmod/cram": "^1.5.9",
40
40
  "@material-ui/icons": "^4.9.1",
41
41
  "abortable-promise-cache": "^1.1.3",
@@ -61,5 +61,5 @@
61
61
  "publishConfig": {
62
62
  "access": "public"
63
63
  },
64
- "gitHead": "f16afc382629a4a830f9040410a9ff1a3a328918"
64
+ "gitHead": "92455c6021abd69548496a450983d89f8837860d"
65
65
  }
@@ -4,7 +4,11 @@ import {
4
4
  BaseOptions,
5
5
  } from '@jbrowse/core/data_adapters/BaseAdapter'
6
6
  import { Region } from '@jbrowse/core/util/types'
7
- import { checkAbortSignal } from '@jbrowse/core/util'
7
+ import {
8
+ checkAbortSignal,
9
+ bytesForRegions,
10
+ updateStatus,
11
+ } from '@jbrowse/core/util'
8
12
  import { openLocation } from '@jbrowse/core/util/io'
9
13
  import { ObservableCreate } from '@jbrowse/core/util/rxjs'
10
14
  import { Feature } from '@jbrowse/core/util/simpleFeature'
@@ -35,7 +39,6 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
35
39
  const location = readConfObject(this.config, ['index', 'location'])
36
40
  const indexType = readConfObject(this.config, ['index', 'indexType'])
37
41
  const chunkSizeLimit = readConfObject(this.config, 'chunkSizeLimit')
38
- const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
39
42
  const bam = new BamFile({
40
43
  bamFilehandle: openLocation(bamLocation, this.pluginManager),
41
44
  csiFilehandle:
@@ -47,7 +50,7 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
47
50
  ? openLocation(location, this.pluginManager)
48
51
  : undefined,
49
52
  chunkSizeLimit,
50
- fetchSizeLimit,
53
+ fetchSizeLimit: 100_000_000,
51
54
  })
52
55
 
53
56
  const adapterConfig = readConfObject(this.config, 'sequenceAdapter')
@@ -70,40 +73,44 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
70
73
  return bam.getHeaderText(opts)
71
74
  }
72
75
 
73
- private async setup(opts?: BaseOptions) {
74
- // note that derived classes may not provide a BAM directly so this is
75
- // conditional
76
+ private async setupPre(opts?: BaseOptions) {
76
77
  const { statusCallback = () => {} } = opts || {}
77
- if (!this.setupP) {
78
- this.setupP = this.configure()
79
- .then(async ({ bam }) => {
80
- statusCallback('Downloading index')
81
- const samHeader = await bam.getHeader(opts)
82
-
83
- // use the @SQ lines in the header to figure out the
84
- // mapping between ref ref ID numbers and names
85
- const idToName: string[] = []
86
- const nameToId: Record<string, number> = {}
87
- samHeader
88
- .filter(l => l.tag === 'SQ')
89
- .forEach((sqLine, refId) => {
90
- sqLine.data.forEach(item => {
91
- if (item.tag === 'SN') {
92
- // this is the ref name
93
- const refName = item.value
94
- nameToId[refName] = refId
95
- idToName[refId] = refName
96
- }
97
- })
78
+ const { bam } = await this.configure()
79
+ this.samHeader = await updateStatus(
80
+ 'Downloading index',
81
+ statusCallback,
82
+ async () => {
83
+ const samHeader = await bam.getHeader(opts)
84
+
85
+ // use the @SQ lines in the header to figure out the
86
+ // mapping between ref ref ID numbers and names
87
+ const idToName: string[] = []
88
+ const nameToId: Record<string, number> = {}
89
+ samHeader
90
+ .filter(l => l.tag === 'SQ')
91
+ .forEach((sqLine, refId) => {
92
+ sqLine.data.forEach(item => {
93
+ if (item.tag === 'SN') {
94
+ // this is the ref name
95
+ const refName = item.value
96
+ nameToId[refName] = refId
97
+ idToName[refId] = refName
98
+ }
98
99
  })
99
- statusCallback('')
100
- this.samHeader = { idToName, nameToId }
101
- return this.samHeader
102
- })
103
- .catch(e => {
104
- this.setupP = undefined
105
- throw e
106
- })
100
+ })
101
+
102
+ return { idToName, nameToId }
103
+ },
104
+ )
105
+ return this.samHeader
106
+ }
107
+
108
+ private async setup(opts?: BaseOptions) {
109
+ if (!this.setupP) {
110
+ this.setupP = this.setupPre(opts).catch(e => {
111
+ this.setupP = undefined
112
+ throw e
113
+ })
107
114
  }
108
115
  return this.setupP
109
116
  }
@@ -188,6 +195,20 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
188
195
  }, signal)
189
196
  }
190
197
 
198
+ async estimateRegionsStats(regions: Region[], opts?: BaseOptions) {
199
+ const { bam } = await this.configure()
200
+ // @ts-ignore
201
+ const index = bam.index
202
+ // this is a method to avoid calling on htsget adapters
203
+ if (index.filehandle !== '?') {
204
+ const bytes = await bytesForRegions(regions, index)
205
+ const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
206
+ return { bytes, fetchSizeLimit }
207
+ } else {
208
+ return super.estimateRegionsStats(regions, opts)
209
+ }
210
+ }
211
+
191
212
  freeResources(/* { region } */): void {}
192
213
 
193
214
  // depends on setup being called before the BAM constructor
@@ -34,7 +34,6 @@ export default class BamSlightlyLazyFeature implements Feature {
34
34
  }
35
35
 
36
36
  _get_flags(): string {
37
- // @ts-ignore
38
37
  return this.record.flags
39
38
  }
40
39
 
@@ -42,11 +41,6 @@ export default class BamSlightlyLazyFeature implements Feature {
42
41
  return this.record.isReverseComplemented() ? -1 : 1
43
42
  }
44
43
 
45
- _get_read_group_id(): number {
46
- // @ts-ignore
47
- return this.record.readGroupId
48
- }
49
-
50
44
  _get_pair_orientation() {
51
45
  return this.record.isPaired() ? this.record.getPairOrientation() : undefined
52
46
  }
@@ -60,36 +54,35 @@ export default class BamSlightlyLazyFeature implements Feature {
60
54
  return this.record._refID
61
55
  }
62
56
 
63
- _get_next_refName(): string | undefined {
57
+ _get_next_refName() {
64
58
  return this.adapter.refIdToName(this.record._next_refid())
65
59
  }
66
60
 
67
- _get_next_segment_position(): string | undefined {
68
- return this.record.isPaired()
69
- ? `${this.adapter.refIdToName(this.record._next_refid())}:${
70
- this.record._next_pos() + 1
71
- }`
61
+ _get_next_segment_position() {
62
+ const { record, adapter } = this
63
+ return record.isPaired()
64
+ ? `${adapter.refIdToName(record._next_refid())}:${record._next_pos() + 1}`
72
65
  : undefined
73
66
  }
74
67
 
75
- _get_seq(): string {
68
+ _get_seq() {
76
69
  return this.record.getReadBases()
77
70
  }
78
71
 
79
- _get_MD(): string | undefined {
80
- const md = this.record.get('MD')
81
- const seq = this.get('seq')
72
+ _get_MD() {
73
+ const md = this.record.get('MD') as string | undefined
74
+ const seq = this.get('seq') as string
82
75
  if (!md && seq && this.ref) {
83
76
  return generateMD(this.ref, this.record.getReadBases(), this.get('CIGAR'))
84
77
  }
85
78
  return md
86
79
  }
87
80
 
88
- qualRaw(): Buffer | undefined {
81
+ qualRaw() {
89
82
  return this.record.qualRaw()
90
83
  }
91
84
 
92
- set(): void {}
85
+ set() {}
93
86
 
94
87
  tags() {
95
88
  const properties = Object.getOwnPropertyNames(
@@ -115,7 +108,7 @@ export default class BamSlightlyLazyFeature implements Feature {
115
108
  ]
116
109
  }
117
110
 
118
- id(): string {
111
+ id() {
119
112
  return `${this.adapter.id}-${this.record.id()}`
120
113
  }
121
114
 
@@ -130,19 +123,19 @@ export default class BamSlightlyLazyFeature implements Feature {
130
123
  return this.record.get(field)
131
124
  }
132
125
 
133
- _get_refName(): string | undefined {
126
+ _get_refName() {
134
127
  return this.adapter.refIdToName(this.record.seq_id())
135
128
  }
136
129
 
137
- parent(): undefined {
130
+ parent() {
138
131
  return undefined
139
132
  }
140
133
 
141
- children(): undefined {
134
+ children() {
142
135
  return undefined
143
136
  }
144
137
 
145
- pairedFeature(): boolean {
138
+ pairedFeature() {
146
139
  return false
147
140
  }
148
141
 
@@ -163,7 +156,7 @@ export default class BamSlightlyLazyFeature implements Feature {
163
156
  } = {
164
157
  cigarAttributeName: 'CIGAR',
165
158
  },
166
- ): Mismatch[] {
159
+ ) {
167
160
  const { cigarAttributeName } = opts
168
161
  let mismatches: Mismatch[] = []
169
162
  let cigarOps: string[] = []
@@ -185,7 +178,7 @@ export default class BamSlightlyLazyFeature implements Feature {
185
178
  }: {
186
179
  cigarAttributeName?: string
187
180
  mdAttributeName?: string
188
- } = {}): Mismatch[] {
181
+ } = {}) {
189
182
  let mismatches: Mismatch[] = []
190
183
  let cigarOps: string[] = []
191
184
 
@@ -25,11 +25,11 @@ export default types.late(() =>
25
25
  }),
26
26
  chunkSizeLimit: {
27
27
  type: 'number',
28
- defaultValue: 100000000,
28
+ defaultValue: 100_000_000,
29
29
  },
30
30
  fetchSizeLimit: {
31
31
  type: 'number',
32
- defaultValue: 500000000,
32
+ defaultValue: 5_000_000,
33
33
  },
34
34
  sequenceAdapter: {
35
35
  type: 'frozen',
@@ -24,14 +24,13 @@ interface Header {
24
24
  }
25
25
 
26
26
  export default class CramAdapter extends BaseFeatureDataAdapter {
27
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
- private cram: any
27
+ samHeader: Header = {}
29
28
 
30
- private setupP?: Promise<Header>
31
-
32
- private sequenceAdapter?: BaseFeatureDataAdapter
33
-
34
- public samHeader: Header = {}
29
+ private setupP?: Promise<{
30
+ samHeader: Header
31
+ cram: any // eslint-disable-line @typescript-eslint/no-explicit-any
32
+ sequenceAdapter: any // eslint-disable-line @typescript-eslint/no-explicit-any
33
+ }>
35
34
 
36
35
  // maps a refname to an id
37
36
  private seqIdToRefName: string[] | undefined
@@ -48,14 +47,16 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
48
47
  if (!craiLocation) {
49
48
  throw new Error('missing craiLocation argument')
50
49
  }
51
- this.cram = new IndexedCramFile({
50
+
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ const cram: any = new IndexedCramFile({
52
53
  cramFilehandle: openLocation(cramLocation, this.pluginManager),
53
54
  index: new CraiIndex({
54
55
  filehandle: openLocation(craiLocation, this.pluginManager),
55
56
  }),
56
57
  seqFetch: this.seqFetch.bind(this),
57
58
  checkSequenceMD5: false,
58
- fetchSizeLimit: this.config.fetchSizeLimit || 600000000,
59
+ fetchSizeLimit: 200_000_000, // just make this a large size to avoid hitting it
59
60
  })
60
61
  // instantiate the sequence adapter
61
62
  const sequenceAdapterType = readConfObject(this.config, [
@@ -67,62 +68,55 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
67
68
  throw new Error('Error getting subadapter')
68
69
  }
69
70
 
70
- const { dataAdapter } = await this.getSubAdapter(
71
- readConfObject(this.config, 'sequenceAdapter'),
72
- )
73
- if (dataAdapter instanceof BaseFeatureDataAdapter) {
74
- this.sequenceAdapter = dataAdapter
75
- } else {
71
+ const seqConf = readConfObject(this.config, 'sequenceAdapter')
72
+ const { dataAdapter: sequenceAdapter } = await this.getSubAdapter(seqConf)
73
+
74
+ if (!(sequenceAdapter instanceof BaseFeatureDataAdapter)) {
76
75
  throw new Error(
77
76
  `CRAM feature adapters cannot use sequence adapters of type '${sequenceAdapterType}'`,
78
77
  )
79
78
  }
80
- return { sequenceAdapter: this.sequenceAdapter }
79
+
80
+ return { cram, sequenceAdapter }
81
81
  }
82
82
 
83
83
  async getHeader(opts?: BaseOptions) {
84
- await this.configure()
85
- return this.cram.cram.getHeaderText(opts)
84
+ const { cram } = await this.configure()
85
+ return cram.cram.getHeaderText(opts)
86
86
  }
87
87
 
88
88
  private async seqFetch(seqId: number, start: number, end: number) {
89
89
  start -= 1 // convert from 1-based closed to interbase
90
90
 
91
- const refSeqStore = this.sequenceAdapter
92
- if (!refSeqStore) {
93
- return undefined
94
- }
91
+ const { sequenceAdapter } = await this.configure()
95
92
  const refName = this.refIdToOriginalName(seqId) || this.refIdToName(seqId)
96
93
  if (!refName) {
97
94
  return undefined
98
95
  }
99
96
 
100
- const features = refSeqStore.getFeatures(
101
- {
97
+ const seqChunks = await sequenceAdapter
98
+ .getFeatures({
102
99
  refName,
103
100
  start,
104
101
  end,
105
102
  assemblyName: '',
106
- },
107
- {},
108
- )
109
-
110
- const seqChunks = await features.pipe(toArray()).toPromise()
103
+ })
104
+ .pipe(toArray())
105
+ .toPromise()
111
106
 
112
- const trimmed: string[] = []
113
- seqChunks
107
+ const sequence = seqChunks
114
108
  .sort((a, b) => a.get('start') - b.get('start'))
115
- .forEach((chunk: Feature) => {
109
+ .map(chunk => {
116
110
  const chunkStart = chunk.get('start')
117
111
  const chunkEnd = chunk.get('end')
118
112
  const trimStart = Math.max(start - chunkStart, 0)
119
113
  const trimEnd = Math.min(end - chunkStart, chunkEnd - chunkStart)
120
114
  const trimLength = trimEnd - trimStart
121
115
  const chunkSeq = chunk.get('seq') || chunk.get('residues')
122
- trimmed.push(chunkSeq.substr(trimStart, trimLength))
116
+ return chunkSeq.substr(trimStart, trimLength)
123
117
  })
118
+ .join('')
124
119
 
125
- const sequence = trimmed.join('')
126
120
  if (sequence.length !== end - start) {
127
121
  throw new Error(
128
122
  `sequence fetch failed: fetching ${refName}:${(
@@ -135,60 +129,56 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
135
129
  return sequence
136
130
  }
137
131
 
138
- private async setup(opts?: BaseOptions) {
132
+ private async setupPre(opts?: BaseOptions) {
139
133
  const { statusCallback = () => {} } = opts || {}
140
- if (!this.setupP) {
141
- this.setupP = this.configure()
142
- .then(async () => {
143
- statusCallback('Downloading index')
144
- const samHeader: HeaderLine[] = await this.cram.cram.getSamHeader(
145
- opts?.signal,
146
- )
147
-
148
- // use the @SQ lines in the header to figure out the
149
- // mapping between ref ID numbers and names
150
- const idToName: string[] = []
151
- const nameToId: Record<string, number> = {}
152
- samHeader
153
- .filter(l => l.tag === 'SQ')
154
- .forEach((sqLine, refId) => {
155
- sqLine.data.forEach(item => {
156
- if (item.tag === 'SN') {
157
- // this is the ref name
158
- const refName = item.value
159
- nameToId[refName] = refId
160
- idToName[refId] = refName
161
- }
162
- })
163
- })
164
-
165
- const readGroups = samHeader
166
- .filter(l => l.tag === 'RG')
167
- .map(rgLine => rgLine.data.find(item => item.tag === 'ID')?.value)
168
-
169
- if (idToName.length) {
170
- this.samHeader = { idToName, nameToId, readGroups }
134
+ const configured = await this.configure()
135
+ statusCallback('Downloading index')
136
+ const { cram } = configured
137
+ const samHeader: HeaderLine[] = await cram.cram.getSamHeader(opts?.signal)
138
+
139
+ // use the @SQ lines in the header to figure out the
140
+ // mapping between ref ID numbers and names
141
+ const idToName: string[] = []
142
+ const nameToId: Record<string, number> = {}
143
+ samHeader
144
+ .filter(l => l.tag === 'SQ')
145
+ .forEach((sqLine, refId) => {
146
+ sqLine.data.forEach(item => {
147
+ if (item.tag === 'SN') {
148
+ // this is the ref name
149
+ const refName = item.value
150
+ nameToId[refName] = refId
151
+ idToName[refId] = refName
171
152
  }
172
- statusCallback('')
173
- return this.samHeader
174
- })
175
- .catch(e => {
176
- this.setupP = undefined
177
- throw e
178
153
  })
154
+ })
155
+
156
+ const readGroups = samHeader
157
+ .filter(l => l.tag === 'RG')
158
+ .map(rgLine => rgLine.data.find(item => item.tag === 'ID')?.value)
159
+
160
+ const data = { idToName, nameToId, readGroups }
161
+ statusCallback('')
162
+ this.samHeader = data
163
+ return { samHeader: data, ...configured }
164
+ }
165
+
166
+ private async setup(opts?: BaseOptions) {
167
+ if (!this.setupP) {
168
+ this.setupP = this.setupPre(opts).catch(e => {
169
+ this.setupP = undefined
170
+ throw e
171
+ })
179
172
  }
180
173
  return this.setupP
181
174
  }
182
175
 
183
176
  async getRefNames(opts?: BaseOptions) {
184
- await this.setup(opts)
185
- if (this.samHeader.idToName) {
186
- return this.samHeader.idToName
187
- }
188
- if (this.sequenceAdapter) {
189
- return this.sequenceAdapter.getRefNames()
177
+ const { samHeader } = await this.setup(opts)
178
+ if (!samHeader.idToName) {
179
+ throw new Error('CRAM file has no header lines')
190
180
  }
191
- throw new Error('unable to get refnames')
181
+ return samHeader.idToName
192
182
  }
193
183
 
194
184
  // use info from the SAM header if possible, but fall back to using
@@ -227,27 +217,24 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
227
217
  const { refName, start, end, originalRefName } = region
228
218
 
229
219
  return ObservableCreate<Feature>(async observer => {
230
- await this.setup(opts)
231
- if (this.sequenceAdapter && !this.seqIdToRefName) {
232
- this.seqIdToRefName = await this.sequenceAdapter.getRefNames(opts)
220
+ const { cram, sequenceAdapter } = await this.setup(opts)
221
+ statusCallback('Downloading alignments')
222
+ if (!this.seqIdToRefName) {
223
+ this.seqIdToRefName = await sequenceAdapter.getRefNames(opts)
233
224
  }
234
225
  const refId = this.refNameToId(refName)
235
226
  if (refId !== undefined) {
236
227
  if (originalRefName) {
237
228
  this.seqIdToOriginalRefName[refId] = originalRefName
238
229
  }
239
- statusCallback('Downloading alignments')
240
- const records = await this.cram.getRecordsForRange(
241
- refId,
242
- start,
243
- end,
244
- opts,
245
- )
230
+ const records = await cram.getRecordsForRange(refId, start, end, opts)
246
231
  checkAbortSignal(signal)
247
232
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
248
233
  records.forEach((record: any) => {
249
234
  observer.next(this.cramRecordToFeature(record))
250
235
  })
236
+ } else {
237
+ console.warn('Unknown refName', refName)
251
238
  }
252
239
  statusCallback('')
253
240
  observer.complete()
@@ -256,8 +243,35 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
256
243
 
257
244
  freeResources(/* { region } */): void {}
258
245
 
259
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
260
- cramRecordToFeature(record: any): Feature {
246
+ cramRecordToFeature(record: unknown) {
261
247
  return new CramSlightlyLazyFeature(record, this)
262
248
  }
249
+
250
+ // we return the configured fetchSizeLimit, and the bytes for the region
251
+ async estimateRegionsStats(regions: Region[], opts?: BaseOptions) {
252
+ const bytes = await this.bytesForRegions(regions, opts)
253
+ const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
254
+ return {
255
+ bytes,
256
+ fetchSizeLimit,
257
+ }
258
+ }
259
+
260
+ /**
261
+ * get the approximate number of bytes queried from the file for the given
262
+ * query regions
263
+ * @param regions - list of query regions
264
+ */
265
+ private async bytesForRegions(regions: Region[], _opts?: BaseOptions) {
266
+ const { cram } = await this.configure()
267
+ const blockResults = await Promise.all(
268
+ regions.map(region => {
269
+ const { refName, start, end } = region
270
+ const chrId = this.refNameToId(refName)
271
+ return cram.index.getEntriesForRange(chrId, start, end)
272
+ }),
273
+ )
274
+
275
+ return blockResults.flat().reduce((a, b) => a + b.sliceBytes, 0)
276
+ }
263
277
  }
@@ -1,12 +1,16 @@
1
1
  import PluginManager from '@jbrowse/core/PluginManager'
2
2
  import { ConfigurationSchema } from '@jbrowse/core/configuration'
3
+ import { types } from 'mobx-state-tree'
3
4
 
4
5
  export default (pluginManager: PluginManager) => {
5
- const { types } = pluginManager.lib['mobx-state-tree']
6
6
  return types.late(() =>
7
7
  ConfigurationSchema(
8
8
  'CramAdapter',
9
9
  {
10
+ fetchSizeLimit: {
11
+ type: 'number',
12
+ defaultValue: 3_000_000,
13
+ },
10
14
  cramLocation: {
11
15
  type: 'fileLocation',
12
16
  defaultValue: {
@@ -32,6 +32,7 @@ const stateModelFactory = (
32
32
  height: 250,
33
33
  showCoverage: true,
34
34
  showPileup: true,
35
+ userFeatureScreenDensity: types.maybe(types.number),
35
36
  }),
36
37
  )
37
38
  .volatile(() => ({
@@ -126,9 +127,9 @@ const stateModelFactory = (
126
127
  height: self.snpCovHeight,
127
128
  }
128
129
  },
129
- setUserBpPerPxLimit(limit: number) {
130
- self.PileupDisplay.setUserBpPerPxLimit(limit)
131
- self.SNPCoverageDisplay.setUserBpPerPxLimit(limit)
130
+ setUserFeatureScreenDensity(limit: number) {
131
+ self.PileupDisplay.setUserFeatureScreenDensity(limit)
132
+ self.SNPCoverageDisplay.setUserFeatureScreenDensity(limit)
132
133
  },
133
134
  setPileupDisplay(displayConfig: AnyConfigurationModel) {
134
135
  self.PileupDisplay = {
@@ -23,10 +23,10 @@ function PileupConfigFactory(pluginManager: PluginManager) {
23
23
  SvgFeatureRenderer: SvgFeatureRendererConfigSchema,
24
24
  }),
25
25
  renderer: '',
26
- maxDisplayedBpPerPx: {
26
+ maxFeatureScreenDensity: {
27
27
  type: 'number',
28
- description: 'maximum bpPerPx that is displayed in the view',
29
- defaultValue: 100,
28
+ description: 'maximum features per pixel that is displayed in the view',
29
+ defaultValue: 5,
30
30
  },
31
31
  colorScheme: {
32
32
  type: 'stringEnum',
@@ -95,15 +95,11 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
95
95
  colorTagMap: observable.map<string, string>({}),
96
96
  modificationTagMap: observable.map<string, string>({}),
97
97
  ready: false,
98
- currBpPerPx: 0,
99
98
  }))
100
99
  .actions(self => ({
101
100
  setReady(flag: boolean) {
102
101
  self.ready = flag
103
102
  },
104
- setCurrBpPerPx(n: number) {
105
- self.currBpPerPx = n
106
- },
107
103
  setMaxHeight(n: number) {
108
104
  self.trackMaxHeight = n
109
105
  },
@@ -404,16 +400,21 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
404
400
  renderProps() {
405
401
  const view = getContainingView(self) as LGV
406
402
  const {
407
- ready,
408
403
  colorTagMap,
409
404
  modificationTagMap,
410
405
  sortedBy,
411
406
  colorBy,
412
407
  rpcDriverName,
413
408
  } = self
409
+
410
+ const superProps = superRenderProps()
411
+
414
412
  return {
415
- ...superRenderProps(),
416
- notReady: !ready || (sortedBy && self.currBpPerPx !== view.bpPerPx),
413
+ ...superProps,
414
+ notReady:
415
+ superProps.notReady ||
416
+ !self.ready ||
417
+ (sortedBy && self.currBpPerPx !== view.bpPerPx),
417
418
  rpcDriverName,
418
419
  displayModel: self,
419
420
  sortedBy,