@jbrowse/plugin-bed 2.16.0 → 2.17.0

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.
@@ -4,8 +4,9 @@ import { BaseFeatureDataAdapter, } from '@jbrowse/core/data_adapters/BaseAdapter
4
4
  import { openLocation } from '@jbrowse/core/util/io';
5
5
  import { ObservableCreate } from '@jbrowse/core/util/rxjs';
6
6
  import { doesIntersect2, max, min, SimpleFeature, } from '@jbrowse/core/util';
7
+ import { firstValueFrom, toArray } from 'rxjs';
7
8
  // locals
8
- import { isUcscProcessedTranscript, makeBlocks, ucscProcessedTranscript, } from '../util';
9
+ import { featureData2 } from '../util';
9
10
  export default class BigBedAdapter extends BaseFeatureDataAdapter {
10
11
  async configurePre(opts) {
11
12
  const pm = this.pluginManager;
@@ -13,22 +14,42 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
13
14
  filehandle: openLocation(this.getConf('bigBedLocation'), pm),
14
15
  });
15
16
  const header = await bigbed.getHeader(opts);
16
- const parser = new BED({ autoSql: header.autoSql });
17
- return { bigbed, header, parser };
17
+ const parser = new BED({
18
+ autoSql: header.autoSql,
19
+ });
20
+ return {
21
+ bigbed,
22
+ header,
23
+ parser,
24
+ };
18
25
  }
19
26
  async configure(opts) {
20
- if (!this.cached) {
21
- this.cached = this.configurePre(opts).catch((e) => {
22
- this.cached = undefined;
27
+ if (!this.cachedP) {
28
+ this.cachedP = this.configurePre(opts).catch((e) => {
29
+ this.cachedP = undefined;
23
30
  throw e;
24
31
  });
25
32
  }
26
- return this.cached;
33
+ return this.cachedP;
27
34
  }
28
35
  async getRefNames(opts) {
29
36
  const { header } = await this.configure(opts);
30
37
  return Object.keys(header.refsByName);
31
38
  }
39
+ async getData() {
40
+ const refNames = await this.getRefNames();
41
+ const features = [];
42
+ for (const refName of refNames) {
43
+ const f = await firstValueFrom(this.getFeatures({
44
+ assemblyName: 'unknown',
45
+ refName,
46
+ start: 0,
47
+ end: Number.MAX_SAFE_INTEGER,
48
+ }).pipe(toArray()));
49
+ features.push(f);
50
+ }
51
+ return features.flat();
52
+ }
32
53
  async getHeader(opts) {
33
54
  const { parser, header } = await this.configure(opts);
34
55
  const { version, fileType } = header;
@@ -40,7 +61,7 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
40
61
  fields: Object.fromEntries(fields.map(({ name, comment }) => [name, comment])),
41
62
  };
42
63
  }
43
- async getFeaturesHelper(query, opts, observer, allowRedispatch, originalQuery = query) {
64
+ async getFeaturesHelper({ query, opts, observer, allowRedispatch, originalQuery = query, }) {
44
65
  const { signal } = opts;
45
66
  const scoreColumn = this.getConf('scoreColumn');
46
67
  const aggregateField = this.getConf('aggregateField');
@@ -61,7 +82,17 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
61
82
  }
62
83
  }
63
84
  if (maxEnd > query.end || minStart < query.start) {
64
- await this.getFeaturesHelper({ ...query, start: minStart, end: maxEnd }, opts, observer, false, query);
85
+ await this.getFeaturesHelper({
86
+ query: {
87
+ ...query,
88
+ start: minStart,
89
+ end: maxEnd,
90
+ },
91
+ opts,
92
+ observer,
93
+ allowRedispatch: false,
94
+ originalQuery: query,
95
+ });
65
96
  return;
66
97
  }
67
98
  }
@@ -70,62 +101,31 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
70
101
  throw new Error('found uniqueId undefined');
71
102
  }
72
103
  for (const feat of feats) {
73
- const data = parser.parseLine(`${query.refName}\t${feat.start}\t${feat.end}\t${feat.rest}`, { uniqueId: feat.uniqueId });
104
+ const line = `${query.refName}\t${feat.start}\t${feat.end}\t${feat.rest}`;
105
+ const data = parser.parseLine(line, { uniqueId: feat.uniqueId });
74
106
  const aggr = data[aggregateField];
75
107
  if (!parentAggregation[aggr]) {
76
108
  parentAggregation[aggr] = [];
77
109
  }
78
- const { uniqueId, type, chromStart, chromStarts, blockStarts, blockCount, blockSizes, chromEnd, thickStart, thickEnd, chrom, score, ...rest } = data;
79
- const subfeatures = blockCount
80
- ? makeBlocks({
81
- chromStarts,
82
- blockStarts,
83
- blockCount,
84
- blockSizes,
85
- uniqueId,
86
- refName: query.refName,
87
- start: feat.start,
88
- })
89
- : [];
90
- if (isUcscProcessedTranscript(data)) {
91
- const f = ucscProcessedTranscript({
92
- ...rest,
93
- uniqueId,
94
- type,
95
- start: feat.start,
96
- end: feat.end,
97
- refName: query.refName,
98
- score: scoreColumn ? +data[scoreColumn] : score,
99
- chromStarts,
100
- blockCount,
101
- blockSizes,
102
- thickStart,
103
- thickEnd,
104
- subfeatures,
105
- });
106
- if (aggr) {
107
- parentAggregation[aggr].push(f);
108
- }
109
- else {
110
- if (doesIntersect2(f.start, f.end, originalQuery.start, originalQuery.end)) {
111
- observer.next(new SimpleFeature({ id: `${this.id}-${uniqueId}`, data: f }));
112
- }
113
- }
110
+ const { uniqueId, type, chrom, chromStart, chromEnd, description, chromStarts: chromStarts2, blockStarts: blockStarts2, blockSizes: blockSizes2, score: score2, blockCount, thickStart, thickEnd, strand, ...rest } = data;
111
+ const f = featureData2({
112
+ ...rest,
113
+ scoreColumn,
114
+ line,
115
+ parser,
116
+ uniqueId,
117
+ start: feat.start,
118
+ end: feat.end,
119
+ refName: query.refName,
120
+ });
121
+ if (aggr) {
122
+ parentAggregation[aggr].push(f);
114
123
  }
115
124
  else {
116
- if (doesIntersect2(feat.start, feat.end, originalQuery.start, originalQuery.end)) {
125
+ if (doesIntersect2(f.start, f.end, originalQuery.start, originalQuery.end)) {
117
126
  observer.next(new SimpleFeature({
118
127
  id: `${this.id}-${uniqueId}`,
119
- data: {
120
- ...rest,
121
- uniqueId,
122
- type,
123
- start: feat.start,
124
- score: scoreColumn ? +data[scoreColumn] : score,
125
- end: feat.end,
126
- refName: query.refName,
127
- subfeatures,
128
- },
128
+ data: f,
129
129
  }));
130
130
  }
131
131
  }
@@ -154,7 +154,12 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
154
154
  getFeatures(query, opts = {}) {
155
155
  return ObservableCreate(async (observer) => {
156
156
  try {
157
- await this.getFeaturesHelper(query, opts, observer, true);
157
+ await this.getFeaturesHelper({
158
+ query,
159
+ opts,
160
+ observer,
161
+ allowRedispatch: true,
162
+ });
158
163
  }
159
164
  catch (e) {
160
165
  observer.error(e);
@@ -0,0 +1,31 @@
1
+ export declare function isBedMethylFeature({ splitLine, start, end, }: {
2
+ splitLine: string[];
3
+ start: number;
4
+ end: number;
5
+ }): boolean;
6
+ export declare function generateBedMethylFeature({ line, uniqueId, refName, start, end, }: {
7
+ line: string;
8
+ uniqueId: string;
9
+ refName: string;
10
+ start: number;
11
+ end: number;
12
+ }): {
13
+ uniqueId: string;
14
+ refName: string;
15
+ start: number;
16
+ end: number;
17
+ code: string | undefined;
18
+ score: string | undefined;
19
+ strand: string | undefined;
20
+ color: string | undefined;
21
+ source: string | undefined;
22
+ n_valid_cov: string | undefined;
23
+ fraction_modified: string | undefined;
24
+ n_mod: string | undefined;
25
+ n_canonical: string | undefined;
26
+ n_other_mod: string | undefined;
27
+ n_delete: string | undefined;
28
+ n_fail: string | undefined;
29
+ n_diff: string | undefined;
30
+ n_nocall: string | undefined;
31
+ };
@@ -0,0 +1,28 @@
1
+ export function isBedMethylFeature({ splitLine, start, end, }) {
2
+ return +(splitLine[6] || 0) === start && +(splitLine[7] || 0) === end;
3
+ }
4
+ export function generateBedMethylFeature({ line, uniqueId, refName, start, end, }) {
5
+ // see
6
+ // https://github.com/nanoporetech/modkit?tab=readme-ov-file#description-of-bedmethyl-output
7
+ const [, , , code, , strand, , , color, n_valid_cov, fraction_modified, n_mod, n_canonical, n_other_mod, n_delete, n_fail, n_diff, n_nocall,] = line.split('\t');
8
+ return {
9
+ uniqueId,
10
+ refName,
11
+ start,
12
+ end,
13
+ code,
14
+ score: fraction_modified,
15
+ strand,
16
+ color,
17
+ source: code,
18
+ n_valid_cov,
19
+ fraction_modified,
20
+ n_mod,
21
+ n_canonical,
22
+ n_other_mod,
23
+ n_delete,
24
+ n_fail,
25
+ n_diff,
26
+ n_nocall,
27
+ };
28
+ }
@@ -0,0 +1,51 @@
1
+ export declare function isRepeatMaskerDescriptionField(desc?: string): desc is string;
2
+ export declare function generateRepeatMaskerFeature({ uniqueId, refName, start, end, description, ...rest }: {
3
+ uniqueId: string;
4
+ refName: string;
5
+ start: number;
6
+ end: number;
7
+ description: string;
8
+ [key: string]: unknown;
9
+ }): {
10
+ uniqueId: string;
11
+ refName: string;
12
+ start: number;
13
+ end: number;
14
+ bitsw_score: string | undefined;
15
+ percent_div: string | undefined;
16
+ percent_del: string | undefined;
17
+ percent_ins: string | undefined;
18
+ query_chr: string | undefined;
19
+ query_begin: string | undefined;
20
+ query_end: string | undefined;
21
+ query_remaining: string | undefined;
22
+ orientation: string | undefined;
23
+ matching_repeat_name: string | undefined;
24
+ matching_repeat_class: string | undefined;
25
+ matching_repeat_begin: string | undefined;
26
+ matching_repeat_end: string | undefined;
27
+ matching_repeat_remaining: string | undefined;
28
+ repeat_id: string | undefined;
29
+ description?: undefined;
30
+ } | {
31
+ uniqueId: string;
32
+ refName: string;
33
+ start: number;
34
+ end: number;
35
+ description: undefined;
36
+ bitsw_score?: undefined;
37
+ percent_div?: undefined;
38
+ percent_del?: undefined;
39
+ percent_ins?: undefined;
40
+ query_chr?: undefined;
41
+ query_begin?: undefined;
42
+ query_end?: undefined;
43
+ query_remaining?: undefined;
44
+ orientation?: undefined;
45
+ matching_repeat_name?: undefined;
46
+ matching_repeat_class?: undefined;
47
+ matching_repeat_begin?: undefined;
48
+ matching_repeat_end?: undefined;
49
+ matching_repeat_remaining?: undefined;
50
+ repeat_id?: undefined;
51
+ };
@@ -0,0 +1,37 @@
1
+ export function isRepeatMaskerDescriptionField(desc) {
2
+ const ret = desc === null || desc === void 0 ? void 0 : desc.trim().split(' ');
3
+ return [0, 1, 2, 3, 5, 6].every(s => (ret === null || ret === void 0 ? void 0 : ret[s]) !== undefined ? !Number.isNaN(+ret[s]) : false);
4
+ }
5
+ function makeRepeatTrackDescription(description) {
6
+ if (isRepeatMaskerDescriptionField(description)) {
7
+ const [bitsw_score, percent_div, percent_del, percent_ins, query_chr, query_begin, query_end, query_remaining, orientation, matching_repeat_name, matching_repeat_class, matching_repeat_begin, matching_repeat_end, matching_repeat_remaining, repeat_id,] = description.trim().split(' ');
8
+ return {
9
+ bitsw_score,
10
+ percent_div,
11
+ percent_del,
12
+ percent_ins,
13
+ query_chr,
14
+ query_begin,
15
+ query_end,
16
+ query_remaining,
17
+ orientation,
18
+ matching_repeat_name,
19
+ matching_repeat_class,
20
+ matching_repeat_begin,
21
+ matching_repeat_end,
22
+ matching_repeat_remaining,
23
+ repeat_id,
24
+ };
25
+ }
26
+ return { description };
27
+ }
28
+ export function generateRepeatMaskerFeature({ uniqueId, refName, start, end, description, ...rest }) {
29
+ return {
30
+ ...rest,
31
+ ...makeRepeatTrackDescription(description),
32
+ uniqueId,
33
+ refName,
34
+ start,
35
+ end,
36
+ };
37
+ }
@@ -0,0 +1,15 @@
1
+ import { MinimalFeature, TranscriptFeat } from './types';
2
+ export declare function isUcscTranscript({ thickStart, blockCount, strand, }: {
3
+ thickStart?: number;
4
+ blockCount?: number;
5
+ strand?: number;
6
+ }): boolean | 0 | undefined;
7
+ export declare function generateUcscTranscript(data: TranscriptFeat): {
8
+ uniqueId: string;
9
+ strand: number;
10
+ type: string;
11
+ refName: string;
12
+ subfeatures: MinimalFeature[];
13
+ start: number;
14
+ end: number;
15
+ };
@@ -0,0 +1,97 @@
1
+ export function isUcscTranscript({ thickStart, blockCount, strand, }) {
2
+ return thickStart && blockCount && strand !== 0;
3
+ }
4
+ export function generateUcscTranscript(data) {
5
+ const { strand = 0, chrom: _1, chromStart: _2, chromEnd: _3, chromStarts, blockStarts, blockSizes, uniqueId, ...rest } = data;
6
+ const { subfeatures: oldSubfeatures, thickStart, thickEnd, blockCount, refName, ...rest2 } = rest;
7
+ const subfeatures = [];
8
+ const feats = oldSubfeatures
9
+ .filter(child => child.type === 'block')
10
+ .sort((a, b) => a.start - b.start);
11
+ for (const block of feats) {
12
+ const start = block.start;
13
+ const end = block.end;
14
+ if (thickStart >= end) {
15
+ // left-side UTR
16
+ subfeatures.push({
17
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
18
+ start,
19
+ end,
20
+ refName,
21
+ });
22
+ }
23
+ else if (thickStart > start && thickStart < end && thickEnd >= end) {
24
+ // UTR | CDS
25
+ subfeatures.push({
26
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
27
+ start,
28
+ end: thickStart,
29
+ refName,
30
+ }, {
31
+ type: 'CDS',
32
+ start: thickStart,
33
+ end,
34
+ refName,
35
+ });
36
+ }
37
+ else if (thickStart <= start && thickEnd >= end) {
38
+ // CDS
39
+ subfeatures.push({
40
+ type: 'CDS',
41
+ start,
42
+ end,
43
+ refName,
44
+ });
45
+ }
46
+ else if (thickStart > start && thickStart < end && thickEnd < end) {
47
+ // UTR | CDS | UTR
48
+ subfeatures.push({
49
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
50
+ start,
51
+ end: thickStart,
52
+ refName,
53
+ }, {
54
+ type: 'CDS',
55
+ start: thickStart,
56
+ end: thickEnd,
57
+ refName,
58
+ }, {
59
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
60
+ start: thickEnd,
61
+ end,
62
+ refName,
63
+ });
64
+ }
65
+ else if (thickStart <= start && thickEnd > start && thickEnd < end) {
66
+ // CDS | UTR
67
+ subfeatures.push({
68
+ type: 'CDS',
69
+ start,
70
+ end: thickEnd,
71
+ refName,
72
+ }, {
73
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
74
+ start: thickEnd,
75
+ end,
76
+ refName,
77
+ });
78
+ }
79
+ else if (thickEnd <= start) {
80
+ // right-side UTR
81
+ subfeatures.push({
82
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
83
+ start,
84
+ end,
85
+ refName,
86
+ });
87
+ }
88
+ }
89
+ return {
90
+ ...rest2,
91
+ uniqueId,
92
+ strand,
93
+ type: 'mRNA',
94
+ refName,
95
+ subfeatures,
96
+ };
97
+ }
package/esm/types.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ export interface MinimalFeature {
2
+ type: string;
3
+ start: number;
4
+ end: number;
5
+ refName: string;
6
+ [key: string]: unknown;
7
+ }
8
+ export interface TranscriptFeat extends MinimalFeature {
9
+ uniqueId: string;
10
+ thickStart: number;
11
+ thickEnd: number;
12
+ blockCount: number;
13
+ blockSizes: number[];
14
+ chromStarts: number[];
15
+ refName: string;
16
+ strand?: number;
17
+ subfeatures: MinimalFeature[];
18
+ }
package/esm/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/esm/util.d.ts CHANGED
@@ -1,37 +1,12 @@
1
1
  import BED from '@gmod/bed';
2
- import { SimpleFeature } from '@jbrowse/core/util';
3
- export interface MinimalFeature {
4
- type: string;
5
- start: number;
6
- end: number;
7
- refName: string;
8
- [key: string]: unknown;
9
- }
10
- export interface TranscriptFeat extends MinimalFeature {
11
- thickStart: number;
12
- thickEnd: number;
13
- blockCount: number;
14
- blockSizes: number[];
15
- chromStarts: number[];
16
- refName: string;
17
- strand?: number;
18
- subfeatures: MinimalFeature[];
19
- }
20
- export declare function ucscProcessedTranscript(feature: TranscriptFeat): TranscriptFeat | {
21
- strand: number;
22
- type: string;
23
- refName: string;
24
- subfeatures: MinimalFeature[];
25
- start: number;
26
- end: number;
27
- };
2
+ import { SimpleFeatureSerialized } from '@jbrowse/core/util';
28
3
  export declare function makeBlocks({ start, uniqueId, refName, chromStarts, blockCount, blockSizes, blockStarts, }: {
29
4
  blockCount: number;
30
5
  start: number;
31
6
  uniqueId: string;
32
7
  refName: string;
33
8
  chromStarts?: number[];
34
- blockSizes: number[];
9
+ blockSizes?: number[];
35
10
  blockStarts?: number[];
36
11
  }): {
37
12
  uniqueId: string;
@@ -40,9 +15,24 @@ export declare function makeBlocks({ start, uniqueId, refName, chromStarts, bloc
40
15
  refName: string;
41
16
  type: string;
42
17
  }[];
43
- export declare function featureData(line: string, colRef: number, colStart: number, colEnd: number, scoreColumn: string, parser: BED, uniqueId: string, names?: string[]): SimpleFeature;
44
- export declare function isUcscProcessedTranscript(f: {
45
- thickStart?: number;
46
- blockCount?: number;
47
- strand?: number;
48
- }): boolean | 0 | undefined;
18
+ export declare function featureData({ line, colRef, colStart, colEnd, scoreColumn, parser, uniqueId, names, }: {
19
+ line: string;
20
+ colRef: number;
21
+ colStart: number;
22
+ colEnd: number;
23
+ scoreColumn: string;
24
+ parser: BED;
25
+ uniqueId: string;
26
+ names?: string[];
27
+ }): SimpleFeatureSerialized;
28
+ export declare function featureData2({ line, refName, start, end, parser, uniqueId, scoreColumn, names, }: {
29
+ line: string;
30
+ refName: string;
31
+ start: number;
32
+ end: number;
33
+ parser: BED;
34
+ uniqueId: string;
35
+ scoreColumn: string;
36
+ names?: string[];
37
+ }): SimpleFeatureSerialized;
38
+ export declare function arrayify(f?: string | number[]): number[] | undefined;