@jbrowse/plugin-gtf 4.2.1 → 5.0.0-beta.1

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.
@@ -1,28 +1,11 @@
1
1
  import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
- import { IntervalTree } from '@jbrowse/core/util';
2
+ import type { GtfAdapterConfig } from './configSchema.ts';
3
3
  import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
4
- import type { Feature, SimpleFeatureSerialized } from '@jbrowse/core/util';
4
+ import type { Feature } from '@jbrowse/core/util';
5
5
  import type { Region } from '@jbrowse/core/util/types';
6
- import type { Observer } from 'rxjs';
7
- type StatusCallback = (arg: string) => void;
8
- type SimpleFeat = SimpleFeatureSerialized;
9
- export default class GtfAdapter extends BaseFeatureDataAdapter {
10
- calculatedIntervalTreeMap: Record<string, IntervalTree<SimpleFeat>>;
11
- gtfFeatures?: Promise<{
12
- header: string;
13
- intervalTreeMap: Record<string, (sc?: StatusCallback) => IntervalTree<SimpleFeat>>;
14
- }>;
15
- private loadDataP;
6
+ export default class GtfAdapter extends BaseFeatureDataAdapter<GtfAdapterConfig> {
16
7
  private loadData;
17
8
  getRefNames(opts?: BaseOptions): Promise<string[]>;
18
9
  getHeader(opts?: BaseOptions): Promise<string>;
19
10
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
20
- getFeaturesHelper({ query, opts, observer, allowRedispatch, originalQuery, }: {
21
- query: Region;
22
- opts: BaseOptions;
23
- observer: Observer<Feature>;
24
- allowRedispatch: boolean;
25
- originalQuery?: Region;
26
- }): Promise<void>;
27
11
  }
28
- export {};
@@ -1,65 +1,33 @@
1
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
- import { IntervalTree, SimpleFeature, doesIntersect2, fetchAndMaybeUnzip, max, min, } from '@jbrowse/core/util';
1
+ import { BaseFeatureDataAdapter, cachedSetup, } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import { SimpleFeature, fetchAndMaybeUnzip } from '@jbrowse/core/util';
3
3
  import { openLocation } from '@jbrowse/core/util/io';
4
- import { parseLineByLine } from '@jbrowse/core/util/parseLineByLine';
4
+ import { groupLinesByRef, makeFeatureIntervalTreeMap, } from '@jbrowse/core/util/parseLineByLine';
5
5
  import { ObservableCreate } from '@jbrowse/core/util/rxjs';
6
- import { parseStringSync } from 'gtf-nostream';
7
- import { featureData } from "../util.js";
6
+ import { aggregateGtfFeatures, parseGtfToFeatures } from '../util.js';
8
7
  export default class GtfAdapter extends BaseFeatureDataAdapter {
9
- calculatedIntervalTreeMap = {};
10
- gtfFeatures;
11
- async loadDataP(opts) {
12
- const loc = openLocation(this.getConf('gtfLocation'), this.pluginManager);
13
- const buffer = await fetchAndMaybeUnzip(loc, opts);
14
- const headerLines = [];
15
- const featureMap = {};
16
- parseLineByLine(buffer, line => {
17
- if (line.startsWith('#')) {
18
- headerLines.push(line);
19
- }
20
- else if (line.startsWith('>')) {
21
- return false;
22
- }
23
- else {
24
- const ret = line.indexOf('\t');
25
- const refName = line.slice(0, ret);
26
- if (!featureMap[refName]) {
27
- featureMap[refName] = '';
28
- }
29
- featureMap[refName] += `${line}\n`;
30
- }
31
- return true;
32
- }, opts?.statusCallback);
33
- const intervalTreeMap = Object.fromEntries(Object.entries(featureMap).map(([refName, lines]) => [
34
- refName,
35
- (sc) => {
36
- if (!this.calculatedIntervalTreeMap[refName]) {
37
- sc?.('Parsing GTF data');
38
- const intervalTree = new IntervalTree();
39
- for (const obj of parseStringSync(lines)
40
- .flat()
41
- .map((f, i) => featureData(f, `${this.id}-${refName}-${i}`))) {
42
- intervalTree.insert([obj.start, obj.end], obj);
43
- }
44
- this.calculatedIntervalTreeMap[refName] = intervalTree;
45
- }
46
- return this.calculatedIntervalTreeMap[refName];
47
- },
48
- ]));
49
- return {
50
- header: headerLines.join('\n'),
51
- intervalTreeMap,
52
- };
53
- }
54
- async loadData(opts = {}) {
55
- if (!this.gtfFeatures) {
56
- this.gtfFeatures = this.loadDataP(opts).catch((e) => {
57
- this.gtfFeatures = undefined;
58
- throw e;
59
- });
60
- }
61
- return this.gtfFeatures;
62
- }
8
+ // the whole file is resident after one load, so genes are aggregated once
9
+ // here and stored in the interval tree already spanning all their
10
+ // transcripts; getFeatures is then a plain tree search with no per-query
11
+ // aggregation or redispatch. Progress comes from inside the load itself
12
+ // rather than a label wrapped around it
13
+ loadData = cachedSetup({
14
+ setup: async (opts) => {
15
+ const aggregateField = this.getConf('aggregateField');
16
+ const buffer = await fetchAndMaybeUnzip(openLocation(this.getConf('gtfLocation'), this.pluginManager), opts);
17
+ const { headerLines, linesByRef } = groupLinesByRef(buffer, opts.statusCallback);
18
+ const intervalTreeMap = makeFeatureIntervalTreeMap(linesByRef, (lines, refName) => aggregateGtfFeatures({
19
+ feats: parseGtfToFeatures(lines.map(line => ({ line })), (_record, i) => `${this.id}-${refName}-${i}`),
20
+ aggregateField,
21
+ refName,
22
+ idPrefix: this.id,
23
+ // whole-ref bounds so nothing is clipped at load; the tree search
24
+ // in getFeatures does the per-query clipping instead
25
+ regionStart: 0,
26
+ regionEnd: Number.MAX_SAFE_INTEGER,
27
+ }), 'Parsing GTF data');
28
+ return { header: headerLines.join('\n'), intervalTreeMap };
29
+ },
30
+ });
63
31
  async getRefNames(opts = {}) {
64
32
  const { intervalTreeMap } = await this.loadData(opts);
65
33
  return Object.keys(intervalTreeMap);
@@ -71,94 +39,21 @@ export default class GtfAdapter extends BaseFeatureDataAdapter {
71
39
  getFeatures(query, opts = {}) {
72
40
  return ObservableCreate(async (observer) => {
73
41
  try {
74
- await this.getFeaturesHelper({
75
- query,
76
- opts,
77
- observer,
78
- allowRedispatch: true,
79
- });
42
+ const { intervalTreeMap } = await this.loadData(opts);
43
+ const tree = intervalTreeMap[query.refName];
44
+ if (tree) {
45
+ for (const feat of tree(opts.statusCallback).search([
46
+ query.start,
47
+ query.end,
48
+ ])) {
49
+ observer.next(new SimpleFeature({ id: feat.uniqueId, data: feat }));
50
+ }
51
+ }
52
+ observer.complete();
80
53
  }
81
54
  catch (e) {
82
55
  observer.error(e);
83
56
  }
84
57
  }, opts.stopToken);
85
58
  }
86
- async getFeaturesHelper({ query, opts, observer, allowRedispatch, originalQuery = query, }) {
87
- const aggregateField = this.getConf('aggregateField');
88
- const { start, end, refName } = query;
89
- const { intervalTreeMap } = await this.loadData(opts);
90
- const feats = intervalTreeMap[refName]?.(opts.statusCallback).search([
91
- start,
92
- end,
93
- ]);
94
- if (feats) {
95
- if (allowRedispatch && feats.length) {
96
- let minStart = Number.POSITIVE_INFINITY;
97
- let maxEnd = Number.NEGATIVE_INFINITY;
98
- let hasAnyAggregateField = false;
99
- for (const feat of feats) {
100
- if (feat.start < minStart) {
101
- minStart = feat.start;
102
- }
103
- if (feat.end > maxEnd) {
104
- maxEnd = feat.end;
105
- }
106
- if (feat[aggregateField]) {
107
- hasAnyAggregateField = true;
108
- }
109
- }
110
- if (hasAnyAggregateField &&
111
- (maxEnd > query.end || minStart < query.start)) {
112
- await this.getFeaturesHelper({
113
- query: {
114
- ...query,
115
- start: minStart - 500_000,
116
- end: maxEnd + 500_000,
117
- },
118
- opts,
119
- observer,
120
- allowRedispatch: false,
121
- originalQuery: query,
122
- });
123
- return;
124
- }
125
- }
126
- const parentAggregation = {};
127
- for (const feat of feats) {
128
- const aggr = feat[aggregateField];
129
- if (!parentAggregation[aggr]) {
130
- parentAggregation[aggr] = [];
131
- }
132
- if (aggr) {
133
- parentAggregation[aggr].push(feat);
134
- }
135
- else {
136
- observer.next(new SimpleFeature({
137
- id: feat.uniqueId,
138
- data: feat,
139
- }));
140
- }
141
- }
142
- for (const [name, subfeatures] of Object.entries(parentAggregation)) {
143
- const s = min(subfeatures.map(f => f.start));
144
- const e = max(subfeatures.map(f => f.end));
145
- if (doesIntersect2(s, e, originalQuery.start, originalQuery.end)) {
146
- const { uniqueId, strand } = subfeatures[0];
147
- observer.next(new SimpleFeature({
148
- id: `${uniqueId}-parent`,
149
- data: {
150
- type: 'gene',
151
- subfeatures,
152
- strand,
153
- name,
154
- start: s,
155
- end: e,
156
- refName: query.refName,
157
- },
158
- }));
159
- }
160
- }
161
- }
162
- observer.complete();
163
- }
164
59
  }
@@ -1,16 +1,42 @@
1
+ import type { Instance } from '@jbrowse/mobx-state-tree';
2
+ export declare function normalizeSnapshot(snap: Record<string, unknown>): Record<string, unknown>;
3
+ /**
4
+ * #config GtfAdapter
5
+ * #category adapter
6
+ * #trackType FeatureTrack
7
+ * #fileFormat feature | GTF (plain) | Loaded entirely into memory; for small files
8
+ * used to load plain-text GTF files (optionally gzipped). Loads the whole file
9
+ * into memory, so prefer the GtfTabixAdapter for large files.
10
+ *
11
+ * #example
12
+ * The `uri` shorthand works for plain or gzipped GTF:
13
+ * ```js
14
+ * {
15
+ * type: 'GtfAdapter',
16
+ * uri: 'https://example.com/genes.gtf',
17
+ * }
18
+ * ```
19
+ */
1
20
  declare const GtfAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
2
- gtfLocation: {
3
- type: string;
4
- description: string;
5
- defaultValue: {
6
- uri: string;
7
- locationType: string;
21
+ /**
22
+ * #slot
23
+ */
24
+ readonly gtfLocation: {
25
+ readonly type: "fileLocation";
26
+ readonly description: "path to gtf file, also allows for gzipped gtf";
27
+ readonly defaultValue: {
28
+ readonly uri: "/path/to/my.gtf";
29
+ readonly locationType: "UriLocation";
8
30
  };
9
31
  };
10
- aggregateField: {
11
- type: string;
12
- description: string;
13
- defaultValue: string;
32
+ /**
33
+ * #slot
34
+ */
35
+ readonly aggregateField: {
36
+ readonly type: "string";
37
+ readonly description: "attribute naming the parent gene that transcripts are aggregated into. transcripts are grouped by gene_id where the file has one (gene names are not unique within a reference), so this is the gene label, and the grouping key only for files with no gene_id";
38
+ readonly defaultValue: "gene_name";
14
39
  };
15
40
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
41
+ export type GtfAdapterConfig = Instance<typeof GtfAdapter>;
16
42
  export default GtfAdapter;
@@ -1,6 +1,36 @@
1
1
  import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
- function x() { }
2
+ export function normalizeSnapshot(snap) {
3
+ return snap.uri
4
+ ? {
5
+ ...snap,
6
+ gtfLocation: {
7
+ uri: snap.uri,
8
+ baseUri: snap.baseUri,
9
+ },
10
+ }
11
+ : snap;
12
+ }
13
+ /**
14
+ * #config GtfAdapter
15
+ * #category adapter
16
+ * #trackType FeatureTrack
17
+ * #fileFormat feature | GTF (plain) | Loaded entirely into memory; for small files
18
+ * used to load plain-text GTF files (optionally gzipped). Loads the whole file
19
+ * into memory, so prefer the GtfTabixAdapter for large files.
20
+ *
21
+ * #example
22
+ * The `uri` shorthand works for plain or gzipped GTF:
23
+ * ```js
24
+ * {
25
+ * type: 'GtfAdapter',
26
+ * uri: 'https://example.com/genes.gtf',
27
+ * }
28
+ * ```
29
+ */
3
30
  const GtfAdapter = ConfigurationSchema('GtfAdapter', {
31
+ /**
32
+ * #slot
33
+ */
4
34
  gtfLocation: {
5
35
  type: 'fileLocation',
6
36
  description: 'path to gtf file, also allows for gzipped gtf',
@@ -9,23 +39,28 @@ const GtfAdapter = ConfigurationSchema('GtfAdapter', {
9
39
  locationType: 'UriLocation',
10
40
  },
11
41
  },
42
+ /**
43
+ * #slot
44
+ */
12
45
  aggregateField: {
13
46
  type: 'string',
14
- description: 'field used to aggregate multiple transcripts into a single parent gene feature',
47
+ description: 'attribute naming the parent gene that transcripts are aggregated into. transcripts are grouped by gene_id where the file has one (gene names are not unique within a reference), so this is the gene label, and the grouping key only for files with no gene_id',
15
48
  defaultValue: 'gene_name',
16
49
  },
17
50
  }, {
18
51
  explicitlyTyped: true,
19
- preProcessSnapshot: snap => {
20
- return snap.uri
21
- ? {
22
- ...snap,
23
- gtfLocation: {
24
- uri: snap.uri,
25
- baseUri: snap.baseUri,
26
- },
27
- }
28
- : snap;
29
- },
52
+ /**
53
+ * #preProcessSnapshot
54
+ *
55
+ *
56
+ * preprocessor to allow minimal config:
57
+ * ```json
58
+ * {
59
+ * "type": "GtfAdapter",
60
+ * "uri": "yourfile.gtf"
61
+ * }
62
+ * ```
63
+ */
64
+ preProcessSnapshot: normalizeSnapshot,
30
65
  });
31
66
  export default GtfAdapter;
@@ -1,10 +1,11 @@
1
1
  import { AdapterType } from '@jbrowse/core/pluggableElementTypes';
2
- import configSchema from "./configSchema.js";
2
+ import configSchema, { normalizeSnapshot } from './configSchema.js';
3
3
  export default function GtfAdapterF(pluginManager) {
4
4
  pluginManager.addAdapterType(() => new AdapterType({
5
5
  name: 'GtfAdapter',
6
6
  displayName: 'GTF adapter',
7
+ normalizeSnapshot,
7
8
  configSchema,
8
- getAdapterClass: () => import("./GtfAdapter.js").then(r => r.default),
9
+ getAdapterClass: () => import('./GtfAdapter.js').then(r => r.default),
9
10
  }));
10
11
  }
@@ -0,0 +1,12 @@
1
+ import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import type { GtfTabixAdapterConfig } from './configSchema.ts';
3
+ import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
4
+ import type { Feature } from '@jbrowse/core/util';
5
+ import type { Region } from '@jbrowse/core/util/types';
6
+ export default class GtfTabixAdapter extends BaseFeatureDataAdapter<GtfTabixAdapterConfig> {
7
+ private configure;
8
+ getRefNames(opts?: BaseOptions): Promise<string[]>;
9
+ getHeader(opts?: BaseOptions): Promise<string>;
10
+ getRegionByteSize(regions: Region[], opts?: BaseOptions): Promise<number>;
11
+ getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
12
+ }
@@ -0,0 +1,68 @@
1
+ import { TabixIndexedFile } from '@gmod/tabix';
2
+ import { BaseFeatureDataAdapter, cachedSetup, } from '@jbrowse/core/data_adapters/BaseAdapter';
3
+ import { SimpleFeature, downloadStatus } from '@jbrowse/core/util';
4
+ import { sharedBgzfWorkerPool } from '@jbrowse/core/util/bgzfWorkerPool';
5
+ import { decompressedBytesBudget } from '@jbrowse/core/util/cacheBudgets';
6
+ import { openLocation, openTabixIndexFilehandle } from '@jbrowse/core/util/io';
7
+ import { ObservableCreate } from '@jbrowse/core/util/rxjs';
8
+ import { readTabixLinesRedispatched } from '@jbrowse/core/util/tabix';
9
+ import { aggregateGtfFeatures, parseGtfToFeatures } from '../util.js';
10
+ export default class GtfTabixAdapter extends BaseFeatureDataAdapter {
11
+ configure = cachedSetup({
12
+ label: 'Downloading index',
13
+ setup: async (_opts, onProgress) => {
14
+ const gtf = new TabixIndexedFile({
15
+ filehandle: openLocation(this.getConf('gtfGzLocation'), this.pluginManager),
16
+ ...openTabixIndexFilehandle(this.getConf(['index', 'location']), this.getConf(['index', 'indexType']), this.pluginManager),
17
+ chunkCacheBudget: decompressedBytesBudget,
18
+ bgzfWorkerPool: sharedBgzfWorkerPool(),
19
+ });
20
+ return {
21
+ gtf,
22
+ dontRedispatchSet: new Set(this.getConf('dontRedispatch')),
23
+ // the index is a whole-file read, so its byte ticks turn the
24
+ // "Downloading index" label into a determinate bar
25
+ header: await gtf.getHeader({ onProgress }),
26
+ };
27
+ },
28
+ });
29
+ async getRefNames(opts = {}) {
30
+ const { gtf } = await this.configure(opts);
31
+ return downloadStatus('Downloading index', opts.statusCallback, onProgress => gtf.getReferenceSequenceNames({ ...opts, onProgress }));
32
+ }
33
+ async getHeader(opts = {}) {
34
+ const { header } = await this.configure(opts);
35
+ return header;
36
+ }
37
+ // Index-only compressed-byte estimate (no feature download), used by the
38
+ // feature-fetch RPC to short-circuit an over-budget region before pulling
39
+ // every line — see executeRenderFeatureData.
40
+ async getRegionByteSize(regions, opts = {}) {
41
+ const { gtf } = await this.configure(opts);
42
+ return gtf.bytesForRegions(regions, opts);
43
+ }
44
+ getFeatures(query, opts = {}) {
45
+ return ObservableCreate(async (observer) => {
46
+ try {
47
+ const { gtf, dontRedispatchSet } = await this.configure(opts);
48
+ const lines = await readTabixLinesRedispatched(gtf, query, dontRedispatchSet, opts);
49
+ const feats = parseGtfToFeatures(lines, record => `${this.id}-offset-${record.offset}`);
50
+ const aggregated = aggregateGtfFeatures({
51
+ feats,
52
+ aggregateField: this.getConf('aggregateField'),
53
+ refName: query.refName,
54
+ idPrefix: this.id,
55
+ regionStart: query.start,
56
+ regionEnd: query.end,
57
+ });
58
+ for (const feat of aggregated) {
59
+ observer.next(new SimpleFeature({ id: feat.uniqueId, data: feat }));
60
+ }
61
+ observer.complete();
62
+ }
63
+ catch (e) {
64
+ observer.error(e);
65
+ }
66
+ }, opts.stopToken);
67
+ }
68
+ }
@@ -0,0 +1,76 @@
1
+ import type { Instance } from '@jbrowse/mobx-state-tree';
2
+ export declare function normalizeSnapshot(snap: Record<string, unknown>): Record<string, unknown>;
3
+ /**
4
+ * #config GtfTabixAdapter
5
+ * #category adapter
6
+ * #trackType FeatureTrack
7
+ * #fileFormat feature | GTF (tabix)
8
+ * used to load bgzip-compressed, tabix-indexed GTF files
9
+ *
10
+ * #example
11
+ * The `uri` shorthand auto-resolves the `.tbi` index; add `csi: true` for a
12
+ * `.csi` index instead:
13
+ * ```js
14
+ * {
15
+ * type: 'GtfTabixAdapter',
16
+ * uri: 'https://example.com/genes.gtf.gz',
17
+ * }
18
+ * ```
19
+ */
20
+ declare const GtfTabixAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
21
+ /**
22
+ * #slot
23
+ * location of the bgzip-compressed GTF, sorted by position. Must be bgzip
24
+ * rather than plain gzip, which tabix cannot index.
25
+ */
26
+ readonly gtfGzLocation: {
27
+ readonly type: "fileLocation";
28
+ readonly defaultValue: {
29
+ readonly uri: "/path/to/my.gtf.gz";
30
+ readonly locationType: "UriLocation";
31
+ };
32
+ };
33
+ readonly index: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
34
+ readonly indexType: {
35
+ readonly model: import("@jbrowse/mobx-state-tree").ISimpleType<"CSI" | "TBI">;
36
+ readonly type: 'stringEnum';
37
+ readonly defaultValue: 'TBI';
38
+ readonly description: '`TBI` is the usual `tabix` output. `CSI` is required for a reference longer than 512 Mb, which TBI cannot address.';
39
+ };
40
+ readonly location: {
41
+ readonly type: 'fileLocation';
42
+ readonly defaultValue: {
43
+ readonly uri: '/path/to/my.gz.tbi';
44
+ readonly locationType: 'UriLocation';
45
+ };
46
+ readonly description: 'location of the tabix index. Only needed when it is not named `<file>.tbi` (or `.csi`), which is what the `uri` shorthand assumes.';
47
+ };
48
+ }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
49
+ /**
50
+ * #slot
51
+ * the GtfTabixAdapter has to "redispatch" if it fetches a region and
52
+ * features it finds inside that region extend outside the region we
53
+ * requested. you can disable this for certain feature types to avoid
54
+ * fetching e.g. the entire chromosome
55
+ *
56
+ * the defaults are the whole-sequence records the common annotation sources
57
+ * emit: `region` (NCBI), `supercontig`/`scaffold` (Ensembl, for
58
+ * non-chromosomal sequences), plus `chromosome` and `contig`. They span an
59
+ * entire reference and have no children, so letting one expand the fetch
60
+ * would pull a whole chromosome to gain nothing
61
+ */
62
+ readonly dontRedispatch: {
63
+ readonly type: "stringArray";
64
+ readonly defaultValue: readonly ["chromosome", "region", "contig", "supercontig", "scaffold"];
65
+ };
66
+ /**
67
+ * #slot
68
+ */
69
+ readonly aggregateField: {
70
+ readonly type: "string";
71
+ readonly description: "attribute naming the parent gene that transcripts are aggregated into. transcripts are grouped by gene_id where the file has one (gene names are not unique within a reference), so this is the gene label, and the grouping key only for files with no gene_id";
72
+ readonly defaultValue: "gene_name";
73
+ };
74
+ }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
75
+ export type GtfTabixAdapterConfig = Instance<typeof GtfTabixAdapter>;
76
+ export default GtfTabixAdapter;
@@ -0,0 +1,86 @@
1
+ import { ConfigurationSchema, expandTabixShorthand, tabixIndexFields, } from '@jbrowse/core/configuration';
2
+ export function normalizeSnapshot(snap) {
3
+ return expandTabixShorthand(snap, 'gtfGzLocation');
4
+ }
5
+ /**
6
+ * #config GtfTabixAdapter
7
+ * #category adapter
8
+ * #trackType FeatureTrack
9
+ * #fileFormat feature | GTF (tabix)
10
+ * used to load bgzip-compressed, tabix-indexed GTF files
11
+ *
12
+ * #example
13
+ * The `uri` shorthand auto-resolves the `.tbi` index; add `csi: true` for a
14
+ * `.csi` index instead:
15
+ * ```js
16
+ * {
17
+ * type: 'GtfTabixAdapter',
18
+ * uri: 'https://example.com/genes.gtf.gz',
19
+ * }
20
+ * ```
21
+ */
22
+ const GtfTabixAdapter = ConfigurationSchema('GtfTabixAdapter', {
23
+ /**
24
+ * #slot
25
+ * location of the bgzip-compressed GTF, sorted by position. Must be bgzip
26
+ * rather than plain gzip, which tabix cannot index.
27
+ */
28
+ gtfGzLocation: {
29
+ type: 'fileLocation',
30
+ defaultValue: {
31
+ uri: '/path/to/my.gtf.gz',
32
+ locationType: 'UriLocation',
33
+ },
34
+ },
35
+ index: ConfigurationSchema('TabixIndex', { ...tabixIndexFields }),
36
+ /**
37
+ * #slot
38
+ * the GtfTabixAdapter has to "redispatch" if it fetches a region and
39
+ * features it finds inside that region extend outside the region we
40
+ * requested. you can disable this for certain feature types to avoid
41
+ * fetching e.g. the entire chromosome
42
+ *
43
+ * the defaults are the whole-sequence records the common annotation sources
44
+ * emit: `region` (NCBI), `supercontig`/`scaffold` (Ensembl, for
45
+ * non-chromosomal sequences), plus `chromosome` and `contig`. They span an
46
+ * entire reference and have no children, so letting one expand the fetch
47
+ * would pull a whole chromosome to gain nothing
48
+ */
49
+ dontRedispatch: {
50
+ type: 'stringArray',
51
+ defaultValue: [
52
+ 'chromosome',
53
+ 'region',
54
+ 'contig',
55
+ 'supercontig',
56
+ 'scaffold',
57
+ ],
58
+ },
59
+ /**
60
+ * #slot
61
+ */
62
+ aggregateField: {
63
+ type: 'string',
64
+ description: 'attribute naming the parent gene that transcripts are aggregated into. transcripts are grouped by gene_id where the file has one (gene names are not unique within a reference), so this is the gene label, and the grouping key only for files with no gene_id',
65
+ defaultValue: 'gene_name',
66
+ },
67
+ }, {
68
+ explicitlyTyped: true,
69
+ /**
70
+ * #preProcessSnapshot
71
+ *
72
+ *
73
+ * preprocessor to allow minimal config, assumes tbi index at
74
+ * yourfile.gtf.gz.tbi (or .csi if csi:true):
75
+ *
76
+ * ```json
77
+ * {
78
+ * "type": "GtfTabixAdapter",
79
+ * "uri": "yourfile.gtf.gz",
80
+ * "csi": true
81
+ * }
82
+ * ```
83
+ */
84
+ preProcessSnapshot: normalizeSnapshot,
85
+ });
86
+ export default GtfTabixAdapter;
@@ -0,0 +1,2 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ export default function GtfTabixAdapterF(pluginManager: PluginManager): void;
@@ -0,0 +1,11 @@
1
+ import { AdapterType } from '@jbrowse/core/pluggableElementTypes';
2
+ import configSchema, { normalizeSnapshot } from './configSchema.js';
3
+ export default function GtfTabixAdapterF(pluginManager) {
4
+ pluginManager.addAdapterType(() => new AdapterType({
5
+ name: 'GtfTabixAdapter',
6
+ displayName: 'GTF tabix adapter',
7
+ normalizeSnapshot,
8
+ configSchema,
9
+ getAdapterClass: () => import('./GtfTabixAdapter.js').then(r => r.default),
10
+ }));
11
+ }
package/esm/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import Plugin from '@jbrowse/core/Plugin';
2
- import GtfAdapterF from "./GtfAdapter/index.js";
3
- import GuessAdapterF from "./GuessAdapter/index.js";
2
+ import GtfAdapterF from './GtfAdapter/index.js';
3
+ import GtfTabixAdapterF from './GtfTabixAdapter/index.js';
4
4
  export default class GtfPlugin extends Plugin {
5
5
  name = 'GTFPlugin';
6
6
  install(pluginManager) {
7
7
  GtfAdapterF(pluginManager);
8
- GuessAdapterF(pluginManager);
8
+ GtfTabixAdapterF(pluginManager);
9
9
  }
10
10
  }