@jbrowse/plugin-bed 4.3.0 → 5.0.0-beta.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.
Files changed (56) hide show
  1. package/esm/BedAdapter/BedAdapter.d.ts +4 -19
  2. package/esm/BedAdapter/BedAdapter.js +31 -88
  3. package/esm/BedAdapter/configSchema.d.ts +89 -40
  4. package/esm/BedAdapter/configSchema.js +65 -13
  5. package/esm/BedAdapter/index.js +4 -2
  6. package/esm/BedGraphAdapter/BedGraphAdapter.d.ts +4 -9
  7. package/esm/BedGraphAdapter/BedGraphAdapter.js +31 -85
  8. package/esm/BedGraphAdapter/configSchema.d.ts +35 -9
  9. package/esm/BedGraphAdapter/configSchema.js +47 -12
  10. package/esm/BedGraphAdapter/index.js +3 -2
  11. package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.d.ts +5 -5
  12. package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.js +62 -57
  13. package/esm/BedGraphTabixAdapter/configSchema.d.ts +48 -19
  14. package/esm/BedGraphTabixAdapter/configSchema.js +42 -34
  15. package/esm/BedGraphTabixAdapter/index.js +3 -2
  16. package/esm/BedTabixAdapter/BedTabixAdapter.d.ts +34 -38
  17. package/esm/BedTabixAdapter/BedTabixAdapter.js +82 -71
  18. package/esm/BedTabixAdapter/configSchema.d.ts +73 -31
  19. package/esm/BedTabixAdapter/configSchema.js +53 -35
  20. package/esm/BedTabixAdapter/index.js +4 -2
  21. package/esm/BedpeAdapter/BedpeAdapter.d.ts +4 -10
  22. package/esm/BedpeAdapter/BedpeAdapter.js +28 -69
  23. package/esm/BedpeAdapter/configSchema.d.ts +33 -10
  24. package/esm/BedpeAdapter/configSchema.js +45 -12
  25. package/esm/BedpeAdapter/index.js +4 -2
  26. package/esm/BedpeAdapter/util.js +22 -23
  27. package/esm/BigBedAdapter/BigBedAdapter.d.ts +8 -7
  28. package/esm/BigBedAdapter/BigBedAdapter.js +100 -107
  29. package/esm/BigBedAdapter/configSchema.d.ts +52 -17
  30. package/esm/BigBedAdapter/configSchema.js +53 -12
  31. package/esm/BigBedAdapter/index.js +3 -2
  32. package/esm/StarFusionAdapter/StarFusionAdapter.d.ts +15 -0
  33. package/esm/StarFusionAdapter/StarFusionAdapter.js +121 -0
  34. package/esm/StarFusionAdapter/configSchema.d.ts +31 -0
  35. package/esm/StarFusionAdapter/configSchema.js +52 -0
  36. package/esm/StarFusionAdapter/index.d.ts +2 -0
  37. package/esm/StarFusionAdapter/index.js +12 -0
  38. package/esm/adapterUtil.d.ts +5 -0
  39. package/esm/adapterUtil.js +28 -0
  40. package/esm/bedGraphUtil.d.ts +10 -0
  41. package/esm/bedGraphUtil.js +18 -0
  42. package/esm/generateBedMethylFeature.d.ts +1 -1
  43. package/esm/generateBedMethylFeature.js +19 -6
  44. package/esm/generateRastairMethylFeature.d.ts +28 -0
  45. package/esm/generateRastairMethylFeature.js +38 -0
  46. package/esm/generateRepeatMaskerFeature.d.ts +4 -51
  47. package/esm/generateRepeatMaskerFeature.js +28 -41
  48. package/esm/generateUcscTranscript.d.ts +15 -7
  49. package/esm/generateUcscTranscript.js +58 -28
  50. package/esm/index.d.ts +2 -0
  51. package/esm/index.js +10 -8
  52. package/esm/util.d.ts +32 -27
  53. package/esm/util.js +127 -97
  54. package/package.json +9 -13
  55. package/esm/GuessAdapter/index.d.ts +0 -2
  56. package/esm/GuessAdapter/index.js +0 -71
@@ -1,32 +1,17 @@
1
- import BED from '@gmod/bed';
2
1
  import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
3
2
  import { IntervalTree } from '@jbrowse/core/util';
3
+ import type { BedAdapterConfig } from './configSchema.ts';
4
4
  import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
5
5
  import type { Feature, Region } from '@jbrowse/core/util';
6
- export default class BedAdapter extends BaseFeatureDataAdapter {
7
- protected bedFeatures?: Promise<{
6
+ export default class BedAdapter extends BaseFeatureDataAdapter<BedAdapterConfig> {
7
+ loadData: (opts?: BaseOptions) => Promise<{
8
8
  header: string;
9
9
  features: Record<string, string[]>;
10
- parser: BED;
11
- columnNames: string[];
12
- scoreColumn: string;
13
- colRef: number;
14
- colStart: number;
15
- colEnd: number;
16
10
  }>;
11
+ private getParser;
17
12
  protected intervalTrees: Record<string, Promise<IntervalTree<Feature> | undefined> | undefined>;
18
13
  static capabilities: string[];
19
14
  private loadDataP;
20
- loadData(opts?: BaseOptions): Promise<{
21
- header: string;
22
- features: Record<string, string[]>;
23
- parser: BED;
24
- columnNames: string[];
25
- scoreColumn: string;
26
- colRef: number;
27
- colStart: number;
28
- colEnd: number;
29
- }>;
30
15
  getRefNames(opts?: BaseOptions): Promise<string[]>;
31
16
  getHeader(opts?: BaseOptions): Promise<string>;
32
17
  getNames(): Promise<string[] | undefined>;
@@ -1,60 +1,24 @@
1
- import BED from '@gmod/bed';
2
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
1
+ import { BaseFeatureDataAdapter, cachedSetup, } from '@jbrowse/core/data_adapters/BaseAdapter';
3
2
  import { IntervalTree, SimpleFeature, fetchAndMaybeUnzip, } from '@jbrowse/core/util';
4
3
  import { openLocation } from '@jbrowse/core/util/io';
5
- import { parseLineByLine } from '@jbrowse/core/util/parseLineByLine';
6
- import { ObservableCreate } from '@jbrowse/core/util/rxjs';
7
- import { featureData } from "../util.js";
4
+ import { intervalTreeFeatures } from '../adapterUtil.js';
5
+ import { bedFeatureLocus, bucketBedLines, featureData, makeParser, resolveColumnNames, } from '../util.js';
8
6
  export default class BedAdapter extends BaseFeatureDataAdapter {
9
- bedFeatures;
7
+ // No `label`: `fetchAndMaybeUnzip` narrates the download from inside.
8
+ loadData = cachedSetup({ setup: opts => this.loadDataP(opts) });
9
+ // the parser needs the column names, which come from the header, so it can
10
+ // only be built once the file is loaded
11
+ getParser = cachedSetup({
12
+ setup: async () => makeParser({
13
+ autoSql: this.getConf('autoSql'),
14
+ columnNames: await this.getNames(),
15
+ }),
16
+ });
10
17
  intervalTrees = {};
11
18
  static capabilities = ['getFeatures', 'getRefNames'];
12
19
  async loadDataP(opts) {
13
- const bedLoc = this.getConf('bedLocation');
14
- const buffer = await fetchAndMaybeUnzip(openLocation(bedLoc, this.pluginManager), opts);
15
- const headerLines = [];
16
- const features = {};
17
- parseLineByLine(buffer, line => {
18
- if (line.startsWith('#')) {
19
- headerLines.push(line);
20
- }
21
- else {
22
- const tab = line.indexOf('\t');
23
- const refName = line.slice(0, tab);
24
- if (!features[refName]) {
25
- features[refName] = [];
26
- }
27
- features[refName].push(line);
28
- }
29
- return true;
30
- }, opts?.statusCallback);
31
- const header = headerLines.join('\n');
32
- const autoSql = this.getConf('autoSql');
33
- const parser = new BED({ autoSql });
34
- const columnNames = this.getConf('columnNames');
35
- const scoreColumn = this.getConf('scoreColumn');
36
- const colRef = this.getConf('colRef');
37
- const colStart = this.getConf('colStart');
38
- const colEnd = this.getConf('colEnd');
39
- return {
40
- header,
41
- features,
42
- parser,
43
- columnNames,
44
- scoreColumn,
45
- colRef,
46
- colStart,
47
- colEnd,
48
- };
49
- }
50
- async loadData(opts = {}) {
51
- if (!this.bedFeatures) {
52
- this.bedFeatures = this.loadDataP(opts).catch((e) => {
53
- this.bedFeatures = undefined;
54
- throw e;
55
- });
56
- }
57
- return this.bedFeatures;
20
+ const buffer = await fetchAndMaybeUnzip(openLocation(this.getConf('bedLocation'), this.pluginManager), opts);
21
+ return bucketBedLines(buffer, opts?.statusCallback);
58
22
  }
59
23
  async getRefNames(opts = {}) {
60
24
  const { features } = await this.loadData(opts);
@@ -65,39 +29,30 @@ export default class BedAdapter extends BaseFeatureDataAdapter {
65
29
  return header;
66
30
  }
67
31
  async getNames() {
68
- const { header, columnNames } = await this.loadData();
69
- if (columnNames.length) {
70
- return columnNames;
71
- }
72
- const defs = header.split(/\n|\r\n|\r/).filter(f => !!f);
73
- const defline = defs.at(-1);
74
- return defline?.includes('\t')
75
- ? defline
76
- .slice(1)
77
- .split('\t')
78
- .map(field => field.trim())
79
- : undefined;
32
+ return resolveColumnNames(this.getConf('columnNames'), async () => (await this.loadData()).header);
80
33
  }
81
34
  async loadFeatureIntervalTreeHelper(refName) {
82
- const { colRef, colStart, colEnd, features, parser, scoreColumn } = await this.loadData();
35
+ const { features } = await this.loadData();
83
36
  const lines = features[refName];
84
37
  if (!lines) {
85
38
  return undefined;
86
39
  }
87
40
  const names = await this.getNames();
41
+ const parser = await this.getParser();
42
+ const scoreColumn = this.getConf('scoreColumn');
43
+ const colRef = this.getConf('colRef');
44
+ const colStart = this.getConf('colStart');
45
+ const colEnd = this.getConf('colEnd');
88
46
  const disableGeneHeuristic = this.getConf('disableGeneHeuristic');
89
47
  const intervalTree = new IntervalTree();
90
48
  for (let i = 0; i < lines.length; i++) {
91
- const line = lines[i];
92
- const uniqueId = `${this.id}-${refName}-${i}`;
49
+ const splitLine = lines[i].split('\t');
93
50
  const feat = new SimpleFeature(featureData({
94
- line,
95
- colRef,
96
- colStart,
97
- colEnd,
51
+ splitLine,
52
+ ...bedFeatureLocus({ splitLine, colRef, colStart, colEnd }),
98
53
  scoreColumn,
99
54
  parser,
100
- uniqueId,
55
+ uniqueId: `${this.id}-${refName}-${i}`,
101
56
  names,
102
57
  disableGeneHeuristic,
103
58
  }));
@@ -106,25 +61,13 @@ export default class BedAdapter extends BaseFeatureDataAdapter {
106
61
  return intervalTree;
107
62
  }
108
63
  async loadFeatureIntervalTree(refName) {
109
- if (!this.intervalTrees[refName]) {
110
- this.intervalTrees[refName] = this.loadFeatureIntervalTreeHelper(refName).catch((e) => {
111
- this.intervalTrees[refName] = undefined;
112
- throw e;
113
- });
114
- }
64
+ this.intervalTrees[refName] ??= this.loadFeatureIntervalTreeHelper(refName).catch((e) => {
65
+ this.intervalTrees[refName] = undefined;
66
+ throw e;
67
+ });
115
68
  return this.intervalTrees[refName];
116
69
  }
117
70
  getFeatures(query, opts = {}) {
118
- return ObservableCreate(async (observer) => {
119
- const { start, end, refName } = query;
120
- const intervalTree = await this.loadFeatureIntervalTree(refName);
121
- const features = intervalTree?.search([start, end]);
122
- if (features) {
123
- for (const f of features) {
124
- observer.next(f);
125
- }
126
- }
127
- observer.complete();
128
- }, opts.stopToken);
71
+ return intervalTreeFeatures(query, opts, refName => this.loadFeatureIntervalTree(refName));
129
72
  }
130
73
  }
@@ -1,46 +1,95 @@
1
+ import type { Instance } from '@jbrowse/mobx-state-tree';
2
+ export declare function normalizeSnapshot(snap: Record<string, unknown>): Record<string, unknown>;
3
+ /**
4
+ * #config BedAdapter
5
+ * #trackType FeatureTrack
6
+ * #fileFormat feature | BED (plain) | Loaded entirely into memory; for small files
7
+ * #gotcha Named BED columns past `name`/`score`/`strand` (`itemRgb`,
8
+ * `thickStart`, ...) are only guaranteed for BED12 or a track with an
9
+ * `autoSql`/`columnNames`. For a BED7-BED11 file JBrowse cannot know what the
10
+ * extra columns mean, so it exposes them generically as `field6`, `field7`,
11
+ * ... and a jexl callback reading `feature.itemRgb` gets `undefined`. Set
12
+ * `columnNames` to refer to them by name.
13
+ *
14
+ * used to load plain-text BED files. Loads the whole file into memory, so
15
+ * prefer the BedTabixAdapter for large files.
16
+ *
17
+ * #example
18
+ * ```js
19
+ * {
20
+ * type: 'BedAdapter',
21
+ * uri: 'https://example.com/features.bed',
22
+ * }
23
+ * ```
24
+ */
1
25
  declare const BedAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
2
- bedLocation: {
3
- type: string;
4
- description: string;
5
- defaultValue: {
6
- uri: string;
7
- locationType: string;
26
+ /**
27
+ * #slot
28
+ */
29
+ readonly bedLocation: {
30
+ readonly type: "fileLocation";
31
+ readonly description: "path to bed file, also allows gzipped bed";
32
+ readonly defaultValue: {
33
+ readonly uri: "/path/to/my.bed.gz";
34
+ readonly locationType: "UriLocation";
8
35
  };
9
36
  };
10
- columnNames: {
11
- type: string;
12
- description: string;
13
- defaultValue: never[];
14
- };
15
- scoreColumn: {
16
- type: string;
17
- description: string;
18
- defaultValue: string;
19
- };
20
- autoSql: {
21
- type: string;
22
- description: string;
23
- defaultValue: string;
24
- };
25
- colRef: {
26
- type: string;
27
- description: string;
28
- defaultValue: number;
29
- };
30
- colStart: {
31
- type: string;
32
- description: string;
33
- defaultValue: number;
34
- };
35
- colEnd: {
36
- type: string;
37
- description: string;
38
- defaultValue: number;
39
- };
40
- disableGeneHeuristic: {
41
- type: string;
42
- description: string;
43
- defaultValue: boolean;
37
+ /**
38
+ * #slot
39
+ */
40
+ readonly columnNames: {
41
+ readonly type: "stringArray";
42
+ readonly description: "List of column names. A column named like a standard BED column is parsed as that column's type (chromStart numeric, blockSizes a numeric list); any other column is text";
43
+ readonly defaultValue: readonly [];
44
+ };
45
+ /**
46
+ * #slot
47
+ */
48
+ readonly scoreColumn: {
49
+ readonly type: "string";
50
+ readonly description: "The column to use as a \"score\" attribute";
51
+ readonly defaultValue: "";
52
+ };
53
+ /**
54
+ * #slot
55
+ */
56
+ readonly autoSql: {
57
+ readonly type: "string";
58
+ readonly description: "The autoSql definition for the data fields in the file";
59
+ readonly defaultValue: "";
60
+ };
61
+ /**
62
+ * #slot
63
+ */
64
+ readonly colRef: {
65
+ readonly type: "number";
66
+ readonly description: "The column to use as a \"refName\" attribute";
67
+ readonly defaultValue: 0;
68
+ };
69
+ /**
70
+ * #slot
71
+ */
72
+ readonly colStart: {
73
+ readonly type: "number";
74
+ readonly description: "The column to use as a \"start\" attribute";
75
+ readonly defaultValue: 1;
76
+ };
77
+ /**
78
+ * #slot
79
+ */
80
+ readonly colEnd: {
81
+ readonly type: "number";
82
+ readonly description: "The column to use as a \"end\" attribute";
83
+ readonly defaultValue: 2;
84
+ };
85
+ /**
86
+ * #slot
87
+ */
88
+ readonly disableGeneHeuristic: {
89
+ readonly type: "boolean";
90
+ readonly description: "Disable the heuristic that auto-detects BED12 features as gene/transcript structures. Useful for files that have BED12-like structure but are not genes (e.g. tandem duplications)";
91
+ readonly defaultValue: false;
44
92
  };
45
93
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
94
+ export type BedAdapterConfig = Instance<typeof BedAdapter>;
46
95
  export default BedAdapter;
@@ -1,6 +1,35 @@
1
1
  import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
- function x() { }
2
+ export function normalizeSnapshot(snap) {
3
+ return snap.uri
4
+ ? { ...snap, bedLocation: { uri: snap.uri, baseUri: snap.baseUri } }
5
+ : snap;
6
+ }
7
+ /**
8
+ * #config BedAdapter
9
+ * #trackType FeatureTrack
10
+ * #fileFormat feature | BED (plain) | Loaded entirely into memory; for small files
11
+ * #gotcha Named BED columns past `name`/`score`/`strand` (`itemRgb`,
12
+ * `thickStart`, ...) are only guaranteed for BED12 or a track with an
13
+ * `autoSql`/`columnNames`. For a BED7-BED11 file JBrowse cannot know what the
14
+ * extra columns mean, so it exposes them generically as `field6`, `field7`,
15
+ * ... and a jexl callback reading `feature.itemRgb` gets `undefined`. Set
16
+ * `columnNames` to refer to them by name.
17
+ *
18
+ * used to load plain-text BED files. Loads the whole file into memory, so
19
+ * prefer the BedTabixAdapter for large files.
20
+ *
21
+ * #example
22
+ * ```js
23
+ * {
24
+ * type: 'BedAdapter',
25
+ * uri: 'https://example.com/features.bed',
26
+ * }
27
+ * ```
28
+ */
3
29
  const BedAdapter = ConfigurationSchema('BedAdapter', {
30
+ /**
31
+ * #slot
32
+ */
4
33
  bedLocation: {
5
34
  type: 'fileLocation',
6
35
  description: 'path to bed file, also allows gzipped bed',
@@ -9,36 +38,57 @@ const BedAdapter = ConfigurationSchema('BedAdapter', {
9
38
  locationType: 'UriLocation',
10
39
  },
11
40
  },
41
+ /**
42
+ * #slot
43
+ */
12
44
  columnNames: {
13
45
  type: 'stringArray',
14
- description: 'List of column names',
46
+ description: "List of column names. A column named like a standard BED column is parsed as that column's type (chromStart numeric, blockSizes a numeric list); any other column is text",
15
47
  defaultValue: [],
16
48
  },
49
+ /**
50
+ * #slot
51
+ */
17
52
  scoreColumn: {
18
53
  type: 'string',
19
54
  description: 'The column to use as a "score" attribute',
20
55
  defaultValue: '',
21
56
  },
57
+ /**
58
+ * #slot
59
+ */
22
60
  autoSql: {
23
61
  type: 'string',
24
62
  description: 'The autoSql definition for the data fields in the file',
25
63
  defaultValue: '',
26
64
  },
65
+ /**
66
+ * #slot
67
+ */
27
68
  colRef: {
28
69
  type: 'number',
29
70
  description: 'The column to use as a "refName" attribute',
30
71
  defaultValue: 0,
31
72
  },
73
+ /**
74
+ * #slot
75
+ */
32
76
  colStart: {
33
77
  type: 'number',
34
78
  description: 'The column to use as a "start" attribute',
35
79
  defaultValue: 1,
36
80
  },
81
+ /**
82
+ * #slot
83
+ */
37
84
  colEnd: {
38
85
  type: 'number',
39
86
  description: 'The column to use as a "end" attribute',
40
87
  defaultValue: 2,
41
88
  },
89
+ /**
90
+ * #slot
91
+ */
42
92
  disableGeneHeuristic: {
43
93
  type: 'boolean',
44
94
  description: 'Disable the heuristic that auto-detects BED12 features as gene/transcript structures. Useful for files that have BED12-like structure but are not genes (e.g. tandem duplications)',
@@ -46,16 +96,18 @@ const BedAdapter = ConfigurationSchema('BedAdapter', {
46
96
  },
47
97
  }, {
48
98
  explicitlyTyped: true,
49
- preProcessSnapshot: snap => {
50
- return snap.uri
51
- ? {
52
- ...snap,
53
- bedLocation: {
54
- uri: snap.uri,
55
- baseUri: snap.baseUri,
56
- },
57
- }
58
- : snap;
59
- },
99
+ /**
100
+ * #preProcessSnapshot
101
+ *
102
+ *
103
+ * preprocessor to allow minimal config:
104
+ * ```json
105
+ * {
106
+ * "type": "BedAdapter",
107
+ * "uri": "yourfile.bed"
108
+ * }
109
+ * ```
110
+ */
111
+ preProcessSnapshot: normalizeSnapshot,
60
112
  });
61
113
  export default BedAdapter;
@@ -1,10 +1,12 @@
1
1
  import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType';
2
- import configSchema from "./configSchema.js";
2
+ import configSchema, { normalizeSnapshot } from './configSchema.js';
3
3
  export default function BedAdapterF(pluginManager) {
4
4
  pluginManager.addAdapterType(() => new AdapterType({
5
5
  name: 'BedAdapter',
6
6
  displayName: 'BED adapter',
7
7
  configSchema,
8
- getAdapterClass: () => import("./BedAdapter.js").then(r => r.default),
8
+ getAdapterClass: () => import('./BedAdapter.js').then(r => r.default),
9
+ locationKey: 'bedLocation',
10
+ normalizeSnapshot,
9
11
  }));
10
12
  }
@@ -1,12 +1,12 @@
1
1
  import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
2
  import { IntervalTree } from '@jbrowse/core/util';
3
+ import type { BedGraphAdapterConfig } from './configSchema.ts';
3
4
  import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
4
5
  import type { Feature, Region } from '@jbrowse/core/util';
5
- export default class BedGraphAdapter extends BaseFeatureDataAdapter {
6
- protected bedFeatures?: Promise<{
6
+ export default class BedGraphAdapter extends BaseFeatureDataAdapter<BedGraphAdapterConfig> {
7
+ loadData: (opts?: BaseOptions) => Promise<{
7
8
  header: string;
8
9
  features: Record<string, string[]>;
9
- columnNames: string[];
10
10
  }>;
11
11
  protected intervalTrees: Record<string, Promise<IntervalTree<Feature> | undefined> | undefined>;
12
12
  getNames(): Promise<string[] | undefined>;
@@ -14,10 +14,5 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
14
14
  getRefNames(opts?: BaseOptions): Promise<string[]>;
15
15
  private loadDataP;
16
16
  loadFeatureIntervalTree(refName: string): Promise<IntervalTree<Feature> | undefined>;
17
- loadData(opts?: BaseOptions): Promise<{
18
- header: string;
19
- features: Record<string, string[]>;
20
- columnNames: string[];
21
- }>;
22
- getFeatures(query: Region, _opts?: BaseOptions): import("rxjs").Observable<Feature>;
17
+ getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
23
18
  }
@@ -1,24 +1,15 @@
1
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
- import { IntervalTree, SimpleFeature, fetchAndMaybeUnzip, } from '@jbrowse/core/util';
1
+ import { BaseFeatureDataAdapter, cachedSetup, } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import { IntervalTree, fetchAndMaybeUnzip } from '@jbrowse/core/util';
3
3
  import { openLocation } from '@jbrowse/core/util/io';
4
- import { parseLineByLine } from '@jbrowse/core/util/parseLineByLine';
5
- import { ObservableCreate } from '@jbrowse/core/util/rxjs';
4
+ import { intervalTreeFeatures } from '../adapterUtil.js';
5
+ import { makeBedGraphFeature } from '../bedGraphUtil.js';
6
+ import { bucketBedLines, resolveColumnNames } from '../util.js';
6
7
  export default class BedGraphAdapter extends BaseFeatureDataAdapter {
7
- bedFeatures;
8
+ // No `label`: `fetchAndMaybeUnzip` narrates the download from inside.
9
+ loadData = cachedSetup({ setup: opts => this.loadDataP(opts) });
8
10
  intervalTrees = {};
9
11
  async getNames() {
10
- const { header, columnNames } = await this.loadData();
11
- if (columnNames.length) {
12
- return columnNames;
13
- }
14
- const defs = header.split(/\n|\r\n|\r/).filter(f => !!f);
15
- const defline = defs.at(-1);
16
- return defline?.includes('\t')
17
- ? defline
18
- .slice(1)
19
- .split('\t')
20
- .map(field => field.trim())
21
- : undefined;
12
+ return resolveColumnNames(this.getConf('columnNames'), async () => (await this.loadData()).header);
22
13
  }
23
14
  async loadFeatureIntervalTreeHelper(refName) {
24
15
  const { features } = await this.loadData();
@@ -26,28 +17,24 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
26
17
  if (!lines) {
27
18
  return undefined;
28
19
  }
29
- const names = (await this.getNames())?.slice(3) || [];
20
+ const names = (await this.getNames())?.slice(3) ?? [];
30
21
  const intervalTree = new IntervalTree();
31
22
  for (let i = 0, l = lines.length; i < l; i++) {
32
- const line = lines[i];
33
- const [refName, s, e, ...rest] = line.split('\t');
23
+ const [refName, s, e, ...rest] = lines[i].split('\t');
24
+ const start = +s;
25
+ const end = +e;
34
26
  for (let j = 0, l2 = rest.length; j < l2; j++) {
35
- const uniqueId = `${this.id}-${refName}-${i}-${j}`;
36
- const start = +s;
37
- const end = +e;
38
- const score = +rest[j];
39
- const source = names[j] || `col${j}`;
40
- if (score) {
41
- intervalTree.insert([start, end], new SimpleFeature({
42
- id: uniqueId,
43
- data: {
44
- refName,
45
- start,
46
- end,
47
- score,
48
- source,
49
- },
50
- }));
27
+ const feat = makeBedGraphFeature({
28
+ uniqueId: `${this.id}-${refName}-${i}-${j}`,
29
+ refName: refName,
30
+ start,
31
+ end,
32
+ names,
33
+ j,
34
+ value: rest[j],
35
+ });
36
+ if (feat) {
37
+ intervalTree.insert([start, end], feat);
51
38
  }
52
39
  }
53
40
  }
@@ -58,58 +45,17 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
58
45
  return Object.keys(features);
59
46
  }
60
47
  async loadDataP(opts = {}) {
61
- const pm = this.pluginManager;
62
- const bedLoc = this.getConf('bedGraphLocation');
63
- const buffer = await fetchAndMaybeUnzip(openLocation(bedLoc, pm), opts);
64
- const features = {};
65
- const headerLines = [];
66
- parseLineByLine(buffer, line => {
67
- if (line.startsWith('#')) {
68
- headerLines.push(line);
69
- }
70
- else {
71
- const tab = line.indexOf('\t');
72
- const refName = line.slice(0, tab);
73
- if (!features[refName]) {
74
- features[refName] = [];
75
- }
76
- features[refName].push(line);
77
- }
78
- return true;
79
- }, opts.statusCallback);
80
- const columnNames = this.getConf('columnNames');
81
- return {
82
- header: headerLines.join('\n'),
83
- features,
84
- columnNames,
85
- };
48
+ const buffer = await fetchAndMaybeUnzip(openLocation(this.getConf('bedGraphLocation'), this.pluginManager), opts);
49
+ return bucketBedLines(buffer, opts.statusCallback);
86
50
  }
87
51
  async loadFeatureIntervalTree(refName) {
88
- if (!this.intervalTrees[refName]) {
89
- this.intervalTrees[refName] = this.loadFeatureIntervalTreeHelper(refName).catch((e) => {
90
- this.intervalTrees[refName] = undefined;
91
- throw e;
92
- });
93
- }
52
+ this.intervalTrees[refName] ??= this.loadFeatureIntervalTreeHelper(refName).catch((e) => {
53
+ this.intervalTrees[refName] = undefined;
54
+ throw e;
55
+ });
94
56
  return this.intervalTrees[refName];
95
57
  }
96
- async loadData(opts = {}) {
97
- if (!this.bedFeatures) {
98
- this.bedFeatures = this.loadDataP(opts).catch((e) => {
99
- this.bedFeatures = undefined;
100
- throw e;
101
- });
102
- }
103
- return this.bedFeatures;
104
- }
105
- getFeatures(query, _opts = {}) {
106
- return ObservableCreate(async (observer) => {
107
- const { start, end, refName } = query;
108
- const intervalTree = await this.loadFeatureIntervalTree(refName);
109
- for (const feature of intervalTree?.search([start, end]) || []) {
110
- observer.next(feature);
111
- }
112
- observer.complete();
113
- });
58
+ getFeatures(query, opts = {}) {
59
+ return intervalTreeFeatures(query, opts, refName => this.loadFeatureIntervalTree(refName));
114
60
  }
115
61
  }