@jbrowse/plugin-gff3 5.0.0-beta.1 → 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.
@@ -22,17 +22,16 @@ export default class Gff3Adapter extends BaseFeatureDataAdapter {
22
22
  // that resident set small (8.5x on GENCODE-shaped input), and the
23
23
  // render path reads only a handful of attributes anyway — see
24
24
  // Gff3Feature.
25
- (lines, refName) => {
26
- const features = parseLinesLazy(lines);
27
- // stamped in place rather than through `{...feature, uniqueId}`:
28
- // these are freshly parsed objects nobody else holds, and the spread
29
- // copied every attribute of every top-level feature in the file to
30
- // add one key
31
- for (let i = 0; i < features.length; i++) {
32
- features[i].uniqueId = `${this.id}-${refName}-${i}`;
33
- }
34
- return features;
35
- }, 'Parsing GFF data');
25
+ //
26
+ // The id the tree entry carries is minted here, beside the feature
27
+ // rather than stamped onto it, so the parsed feature is the library's
28
+ // shape and Gff3Feature serializes it identically for both adapters.
29
+ (lines, refName) => parseLinesLazy(lines).map((feature, i) => ({
30
+ start: feature.start,
31
+ end: feature.end,
32
+ feature,
33
+ uniqueId: `${this.id}-${refName}-${i}`,
34
+ })), 'Parsing GFF data');
36
35
  return { header: headerLines.join('\n'), intervalTreeMap };
37
36
  },
38
37
  });
@@ -52,8 +51,11 @@ export default class Gff3Adapter extends BaseFeatureDataAdapter {
52
51
  const { intervalTreeMap } = await this.loadData(opts);
53
52
  const tree = intervalTreeMap[refName];
54
53
  if (tree) {
55
- for (const f of tree(opts.statusCallback).search([start, end])) {
56
- observer.next(new Gff3Feature(f, f.uniqueId));
54
+ for (const { feature, uniqueId } of tree(opts.statusCallback).search([
55
+ start,
56
+ end,
57
+ ])) {
58
+ observer.next(new Gff3Feature(feature, uniqueId));
57
59
  }
58
60
  }
59
61
  observer.complete();
@@ -1,9 +1,5 @@
1
1
  import type { Feature, SimpleFeatureSerialized } from '@jbrowse/core/util/simpleFeature';
2
2
  import type { LazyGffFeature } from 'gff-nostream';
3
- /** A parsed GFF3 feature carrying the id the adapter minted for it. */
4
- export type IdentifiedGffFeature = LazyGffFeature & {
5
- uniqueId: string;
6
- };
7
3
  /**
8
4
  * A {@link Feature} over a GFF3 feature whose attributes are still raw column-9
9
5
  * text, resolving each one only when something asks for it.
@@ -26,7 +22,7 @@ export declare class Gff3Feature implements Feature {
26
22
  private uniqueId;
27
23
  private parentFeature?;
28
24
  private inflated?;
29
- constructor(data: IdentifiedGffFeature | LazyGffFeature, uniqueId: string, parentFeature?: Feature | undefined);
25
+ constructor(data: LazyGffFeature, uniqueId: string, parentFeature?: Feature | undefined);
30
26
  /**
31
27
  * A switch rather than a lookup table or a `data[name]` fallthrough: the
32
28
  * fixed columns are read tens of times per feature by the layout pass, and
@@ -7,7 +7,7 @@ import { openLocation, openTabixIndexFilehandle } from '@jbrowse/core/util/io';
7
7
  import { doesIntersect2 } from '@jbrowse/core/util/range';
8
8
  import { ObservableCreate } from '@jbrowse/core/util/rxjs';
9
9
  import { readTabixLinesRedispatched } from '@jbrowse/core/util/tabix';
10
- import { parseRecordsLazy } from 'gff-nostream';
10
+ import { hasIdAttribute, parseRecordsLazy } from 'gff-nostream';
11
11
  import { Gff3Feature } from '../Gff3Feature.js';
12
12
  export default class Gff3TabixAdapter extends BaseFeatureDataAdapter {
13
13
  configure = cachedSetup({
@@ -47,7 +47,26 @@ export default class Gff3TabixAdapter extends BaseFeatureDataAdapter {
47
47
  return ObservableCreate(async (observer) => {
48
48
  try {
49
49
  const { gff, dontRedispatchSet } = await this.configure(opts);
50
- const lines = await readTabixLinesRedispatched(gff, query, dontRedispatchSet, opts);
50
+ // The bound exists to reach a record's children, so what disqualifies a
51
+ // record is having none. `ID` answers that exactly — nothing can name a
52
+ // parent that has no name — and gff-nostream decides it with the scan
53
+ // its linker uses, so a record the tree would attach children to is
54
+ // never one the bound leaves out. `dontRedispatch` can only approximate
55
+ // the same question by type: every NCBI `GCF_*_genomic.gff.gz` opens
56
+ // each reference with a chromosome-long `match` record no list named,
57
+ // and none of them has an `ID`. The list covers what `ID` cannot see: a
58
+ // record carrying an `ID` that nothing references, which hosted hg19
59
+ // RefSeq's chromosome-long `region` is.
60
+ const canHaveChildren = (line) => hasIdAttribute(line.line) && !dontRedispatchSet.has(line.type);
61
+ const lines = await readTabixLinesRedispatched(gff, query,
62
+ // A caller reading only top-level features is owed nothing by the
63
+ // flanks and pays for them anyway: they complete SUBFEATURE lists, and
64
+ // a top-level feature overlapping the query is in the query's own read
65
+ // already, since tabix returns every overlapping line and a child is
66
+ // contained in its parent. On an NCBI `GCF_*_genomic.gff.gz`, whose
67
+ // chromosome-long `match` widens the bound at every window, that is
68
+ // 193,008 lines parsed to keep 3 features — 2734 ms against 8 ms.
69
+ opts.topLevelOnly ? () => false : canHaveChildren, opts);
51
70
  // emit only top-level features intersecting the original query. the
52
71
  // byte offset stays on our own record and is used purely to mint a
53
72
  // stable id, so it never pollutes the feature's data
@@ -18,6 +18,11 @@ export declare function normalizeSnapshot(snap: Record<string, unknown>): Record
18
18
  * ```
19
19
  */
20
20
  declare const Gff3TabixAdapter: 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
+ };
21
26
  /**
22
27
  * #slot
23
28
  * location of the bgzip-compressed GFF3, sorted by position. Must be bgzip
@@ -1,4 +1,5 @@
1
1
  import { ConfigurationSchema, expandTabixShorthand, tabixIndexFields, } from '@jbrowse/core/configuration';
2
+ import { densityAdapterConfigSchemaFields } from '@jbrowse/core/data_adapters/BaseAdapter';
2
3
  export function normalizeSnapshot(snap) {
3
4
  return expandTabixShorthand(snap, 'gffGzLocation');
4
5
  }
@@ -56,6 +57,7 @@ const Gff3TabixAdapter = ConfigurationSchema('Gff3TabixAdapter', {
56
57
  'scaffold',
57
58
  ],
58
59
  },
60
+ ...densityAdapterConfigSchemaFields,
59
61
  }, {
60
62
  explicitlyTyped: true,
61
63
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-gff3",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0-beta.2",
4
4
  "type": "module",
5
5
  "description": "JBrowse 2 gff3.",
6
6
  "keywords": [
@@ -23,10 +23,10 @@
23
23
  "dependencies": {
24
24
  "@gmod/tabix": "^3.8.2",
25
25
  "@jbrowse/mobx-state-tree": "^6.5.1",
26
- "gff-nostream": "^5.3.0",
26
+ "gff-nostream": "^5.4.0",
27
27
  "mobx": "^7.0.3",
28
28
  "rxjs": "^7.8.2",
29
- "@jbrowse/core": "^5.0.0-beta.1"
29
+ "@jbrowse/core": "^5.0.0-beta.2"
30
30
  },
31
31
  "publishConfig": {
32
32
  "access": "public"