@jbrowse/plugin-gtf 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.
@@ -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,79 @@
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
+ // The type list alone, and `opts.topLevelOnly` is deliberately not
49
+ // honoured — both for the same reason. A GTF line has no `ID`/`Parent`,
50
+ // so there is no cheap "can this have children"; and a GTF top-level
51
+ // feature is not a line at all, it is synthesized by
52
+ // `aggregateGtfFeatures` from the transcripts in the FETCHED set, whose
53
+ // span it takes from them. So the argument that lets GFF3 skip the
54
+ // flanks — a top-level feature overlapping the query is already in the
55
+ // query's own read — does not transfer, and a narrower bound could draw
56
+ // a gene short rather than merely flat. Nothing is lost by declining:
57
+ // no GTF record shape makes this bound expensive (the fixtures top out
58
+ // at 19 kb, against GFF3's chromosome-long `match`).
59
+ const lines = await readTabixLinesRedispatched(gtf, query, line => !dontRedispatchSet.has(line.type), opts);
60
+ const feats = parseGtfToFeatures(lines, record => `${this.id}-offset-${record.offset}`);
61
+ const aggregated = aggregateGtfFeatures({
62
+ feats,
63
+ aggregateField: this.getConf('aggregateField'),
64
+ refName: query.refName,
65
+ idPrefix: this.id,
66
+ regionStart: query.start,
67
+ regionEnd: query.end,
68
+ });
69
+ for (const feat of aggregated) {
70
+ observer.next(new SimpleFeature({ id: feat.uniqueId, data: feat }));
71
+ }
72
+ observer.complete();
73
+ }
74
+ catch (e) {
75
+ observer.error(e);
76
+ }
77
+ }, opts.stopToken);
78
+ }
79
+ }
@@ -0,0 +1,81 @@
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
+ readonly densityAdapter: {
22
+ readonly type: 'frozen';
23
+ readonly defaultValue: null;
24
+ readonly description: 'optional quantitative sub-adapter (e.g. a BigWigAdapter over a features-per-bin bigWig) drawn as a density band where the region is too large to fetch features; null disables it';
25
+ };
26
+ /**
27
+ * #slot
28
+ * location of the bgzip-compressed GTF, sorted by position. Must be bgzip
29
+ * rather than plain gzip, which tabix cannot index.
30
+ */
31
+ readonly gtfGzLocation: {
32
+ readonly type: "fileLocation";
33
+ readonly defaultValue: {
34
+ readonly uri: "/path/to/my.gtf.gz";
35
+ readonly locationType: "UriLocation";
36
+ };
37
+ };
38
+ readonly index: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
39
+ readonly indexType: {
40
+ readonly model: import("@jbrowse/mobx-state-tree").ISimpleType<"CSI" | "TBI">;
41
+ readonly type: 'stringEnum';
42
+ readonly defaultValue: 'TBI';
43
+ readonly description: '`TBI` is the usual `tabix` output. `CSI` is required for a reference longer than 512 Mb, which TBI cannot address.';
44
+ };
45
+ readonly location: {
46
+ readonly type: 'fileLocation';
47
+ readonly defaultValue: {
48
+ readonly uri: '/path/to/my.gz.tbi';
49
+ readonly locationType: 'UriLocation';
50
+ };
51
+ 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.';
52
+ };
53
+ }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
54
+ /**
55
+ * #slot
56
+ * the GtfTabixAdapter has to "redispatch" if it fetches a region and
57
+ * features it finds inside that region extend outside the region we
58
+ * requested. you can disable this for certain feature types to avoid
59
+ * fetching e.g. the entire chromosome
60
+ *
61
+ * the defaults are the whole-sequence records the common annotation sources
62
+ * emit: `region` (NCBI), `supercontig`/`scaffold` (Ensembl, for
63
+ * non-chromosomal sequences), plus `chromosome` and `contig`. They span an
64
+ * entire reference and have no children, so letting one expand the fetch
65
+ * would pull a whole chromosome to gain nothing
66
+ */
67
+ readonly dontRedispatch: {
68
+ readonly type: "stringArray";
69
+ readonly defaultValue: readonly ["chromosome", "region", "contig", "supercontig", "scaffold"];
70
+ };
71
+ /**
72
+ * #slot
73
+ */
74
+ readonly aggregateField: {
75
+ readonly type: "string";
76
+ 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";
77
+ readonly defaultValue: "gene_name";
78
+ };
79
+ }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
80
+ export type GtfTabixAdapterConfig = Instance<typeof GtfTabixAdapter>;
81
+ export default GtfTabixAdapter;
@@ -0,0 +1,88 @@
1
+ import { ConfigurationSchema, expandTabixShorthand, tabixIndexFields, } from '@jbrowse/core/configuration';
2
+ import { densityAdapterConfigSchemaFields } from '@jbrowse/core/data_adapters/BaseAdapter';
3
+ export function normalizeSnapshot(snap) {
4
+ return expandTabixShorthand(snap, 'gtfGzLocation');
5
+ }
6
+ /**
7
+ * #config GtfTabixAdapter
8
+ * #category adapter
9
+ * #trackType FeatureTrack
10
+ * #fileFormat feature | GTF (tabix)
11
+ * used to load bgzip-compressed, tabix-indexed GTF files
12
+ *
13
+ * #example
14
+ * The `uri` shorthand auto-resolves the `.tbi` index; add `csi: true` for a
15
+ * `.csi` index instead:
16
+ * ```js
17
+ * {
18
+ * type: 'GtfTabixAdapter',
19
+ * uri: 'https://example.com/genes.gtf.gz',
20
+ * }
21
+ * ```
22
+ */
23
+ const GtfTabixAdapter = ConfigurationSchema('GtfTabixAdapter', {
24
+ /**
25
+ * #slot
26
+ * location of the bgzip-compressed GTF, sorted by position. Must be bgzip
27
+ * rather than plain gzip, which tabix cannot index.
28
+ */
29
+ gtfGzLocation: {
30
+ type: 'fileLocation',
31
+ defaultValue: {
32
+ uri: '/path/to/my.gtf.gz',
33
+ locationType: 'UriLocation',
34
+ },
35
+ },
36
+ index: ConfigurationSchema('TabixIndex', { ...tabixIndexFields }),
37
+ /**
38
+ * #slot
39
+ * the GtfTabixAdapter has to "redispatch" if it fetches a region and
40
+ * features it finds inside that region extend outside the region we
41
+ * requested. you can disable this for certain feature types to avoid
42
+ * fetching e.g. the entire chromosome
43
+ *
44
+ * the defaults are the whole-sequence records the common annotation sources
45
+ * emit: `region` (NCBI), `supercontig`/`scaffold` (Ensembl, for
46
+ * non-chromosomal sequences), plus `chromosome` and `contig`. They span an
47
+ * entire reference and have no children, so letting one expand the fetch
48
+ * would pull a whole chromosome to gain nothing
49
+ */
50
+ dontRedispatch: {
51
+ type: 'stringArray',
52
+ defaultValue: [
53
+ 'chromosome',
54
+ 'region',
55
+ 'contig',
56
+ 'supercontig',
57
+ 'scaffold',
58
+ ],
59
+ },
60
+ /**
61
+ * #slot
62
+ */
63
+ aggregateField: {
64
+ type: 'string',
65
+ 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',
66
+ defaultValue: 'gene_name',
67
+ },
68
+ ...densityAdapterConfigSchemaFields,
69
+ }, {
70
+ explicitlyTyped: true,
71
+ /**
72
+ * #preProcessSnapshot
73
+ *
74
+ *
75
+ * preprocessor to allow minimal config, assumes tbi index at
76
+ * yourfile.gtf.gz.tbi (or .csi if csi:true):
77
+ *
78
+ * ```json
79
+ * {
80
+ * "type": "GtfTabixAdapter",
81
+ * "uri": "yourfile.gtf.gz",
82
+ * "csi": true
83
+ * }
84
+ * ```
85
+ */
86
+ preProcessSnapshot: normalizeSnapshot,
87
+ });
88
+ 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
+ }